java并发-synchronized关键字&CAS锁
引言
java中提供了synchronized关键字和java.util.concurrent.locks包下的相关类来进行线程同步
synchronized关键字
在1.6之前synchronized是一个重量级锁,此同步方式同步成本非常高,因为阻塞线程会引起用户态和内核态之间切换。此后jvm对此同步方式进行了许多优化(偏向锁、轻量级锁)可以放心使用,而且因为是java关键字所以也可以享受以后的优化并且不需要更改代码
修饰 | 锁对象 |
---|---|
静态方法 | 当前类对象(class) |
实例方法 | 当前实例(this) |
修饰代码块 | 指定变量 |
synchronized锁膨胀过程 [偏向锁] -> [轻量级锁] -> [重量级锁]
- 偏向锁
偏向锁会在第一次进行同步访问时的将访问线程的线程ID使用CAS记录到互斥变量的Mark word
。
以后每次访问则检查访问线程的ID是否等于Mark word中记录的线程ID,如果等于则可以无锁同步。如果线程ID不相同则说明有其他线程竞争,此时偏向锁会膨胀成轻量级锁
适用场景:自始至终仅有同一线程访问
优点:可以实现无锁同步
缺点:如果很明显有其他线程竞争则很快将膨胀成轻量级锁 - 轻量级锁
加锁:为访问线程创建Lock record
,将锁对象的Mark word
复制到Lock record
称为Displaced mark word
,尝试使用CAS将Mark word
指向Displaced mark word
,如果成功则获取锁成功,否则循环重试
解锁:使用Displaced mark word
替换原对象的Mark word
轻量级锁会在重试次数过多时膨胀成重量级锁
适用场景:线程竞争少,非耗时操作
优点:可以避免线程阻塞造成的造成的用户态和内核态切换成本
缺点:如果进行耗时操作则会循环获取锁占用CPU资源 - 重量级锁
重量级锁会在利用操作系统底层创建互斥量实现,同步时会造成内核态切换
适用场景:线程竞争激烈,耗时操作
优点:线程直接阻塞无CPU资源占用
缺点:线程阻塞,阻塞和唤醒线程耗时可能会超过代码本身执行时间,性能可能低于轻量级锁
ReentrantLock锁
ReentrantLock分为公平锁和非公平锁,内部使用CAS方式实现具体原理类似于轻量级锁
对比
- synchronized
- java关键字,易用
- 无需改代码享受jdk优化
- ReentrantLock
- java类,加锁解锁都是实例方法,可以更灵活的控制
- 提供Condition可以灵活的控制要唤醒的线程
- 可以实现公平锁
- 可以中断等待