Java中synchronized 关键字和volatile关键字
synchronized 语法使用
- 1、静态方法
- 2、实力方法
- 3、代码块
synchronized
(对象){
//TODO
}
-
1、介绍
synchronized关键字是Java提供的一种原子性内置锁,线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候其他线程访问该同步代码块的时会被阻塞挂起。
也就是说,各个线程竞争同一把锁,如果竞争成功,就继续往下执行,否则就被挂起(阻塞在当前行),当此线程正常退出或者抛出异常后(释放对象锁),通知JVM及系统,其他的线程可以来竞争这把锁
而Java中的线程和操作系统中的原生线程是一一对应的,因此当阻塞一个线程时候,需要从用户态切换到内核态来进行阻塞,我们知道切换这个过程是一个耗时 的操作,而synchronized
的使用就会导致上下文切换 -
2、加锁操作
- 对哪一个对象进行加锁操作(一个对象只有一把锁)
- 只有同一个对象,才会存在同步互斥的作用(线程安全的三大特性都能满足)
- 对于
synchronized
内部的代码来说,同一个时间点只有一个线程在执行(不存在并发、并行)- 运行的线程数量越多,性能下降的越快(归还对象锁的时候,就会有很多的线程不停在被唤醒,阻塞状态间进行切换)
![]()
-
3、内存语义
内存语义也就是说可以解决共享变量内存可见性问题。
- 进入
synchronized
块内的语义是:在synchronized
块内使用的变量从线程的工作内存进行清除,这样在synchronized
块内使用的该变量就不会从该线程工作内存获取,而是直接从主内存获取。- 退出代码块的与语义是:将synchronized 块内对共享变量修改刷新到主内存
volatile关键字
该关键字可以保证对一个变量的更新对其他线程立马可见(解决可见性问题)
-
当一个变量被volatile修饰时,线程在写入变量时候,不会把值缓存在寄存器或者其他地方,而是会把值刷新到主内存。当其他线程读取该变量时,会从主内存获取
注意:synchronized和volatile都可以解决可见性问题
两者都可以解决可见性问题,但是使用锁相对于比较笨重,耗费时间,因为它会带来线程上下文切换的时间开销
-
使用volatile的情况
1、当写入变量值不依赖变量的当前值。因为如果依赖当前值,将是获取- 计算 - 写入三步操作,这三步不是原子性的,而volatile不支持原子性。
2、读写变量时没有加锁。因为加锁本身已经保证了可见性问题,就不用volatile修饰