1分钟快速了解——Java几种常见的锁
- 乐观锁
- 悲观锁
- 公平锁
- 非公平锁
- 可重入锁
- 独占锁(写锁)
- 共享锁(写锁)
乐观锁(自旋锁):认为共享资源每次被访问都不会出现安全问题,无需加锁。
CAS
算法和版本号
法就是乐观锁的具体实现。CAS只有当旧值和预期值一样,再将旧值更新为新值。但是CAS存在ABA问题,即对数据进行修改,随后又复原为原值的问题。解决这一问题可以使用版本号法。
悲观锁:认为共享资源每次被访问时都会出现安全问题,故每次获取资源都需要上锁。
synchronized
和ReentrantLock
等独占锁都是悲观锁。
公平锁:锁被释放后,先申请的线程先获得锁。性能比较差,因为CPU唤醒线程具有一定的开销。
非公平锁:锁被释放后,后申请的线程可能先后去锁,是随机的或者按照优先级排序的。性能更好,因为减少了CPU唤醒线程的开销。但是也可能导致饥饿现象(某些线程永远获取不到锁)。
ReentrantLock默认创建的是
非公平锁
,可以通过其构造器传参创建公平锁和非公平锁。
true:公平锁
false:非公平锁
可重入锁:一个线程可以多次获取同一个锁。如一个线程获得了一个锁,这时锁还没有释放,而线程想要再次获取锁是可以的,如果是不可重入锁,就会导致死锁。
synchronized
和ReentrantLock
都是可重入锁。
独占锁(写锁):最多只能同时被一个线程获取。
共享锁(读锁):可以同时被多个线程获取。
synchronized和ReentrantLock的区别
区别 | synchronized | ReentrantLock |
---|---|---|
加锁、解锁 | 自动 | 手动,配合try、finally语句块实现 |
中断等待锁的线程 | 不可中断 | 能够中断 |
超时等待 | 不支持 | 支持 |
支持公平/非公平锁 | 非公平锁 | 构造方法工作两种锁 |
synchronized的锁升级
过程:偏向锁—>轻量级锁—>重量级锁
偏向锁:jdk8默认是轻量级锁,但如果设定了 -XX:BiasedLockingStartupDelay = 0 ,则会获得一把偏向锁,并且锁对象的markword会记录偏向锁线程ID。
轻量级锁:当下一个线程竞争偏向锁时,会判断markword中的偏向锁ID是否为当前线程,如果不相等,偏向锁就会升级会轻量级锁。每个线程再通过CAS自旋的操作获取锁。
重量级锁:如果锁竞争加剧(线程自旋次数获自旋线程数超过某个阈值),轻量级锁升级为重量级锁。申请资源的线程会被挂起,进入到操作系统内核态的等待队列中,等待操作系统调度,然后映射回用户态。