概览

https://tech.meituan.com/2018/11/15/java-lock.html
1、乐观锁、悲观锁
乐观锁:认为冲突不会发生,因此在访问数据时不加锁,只有在修改时才验证数据是否被修改过。
- 应用场景:读多写少。不加锁,可以提升性能。
- 问题:高并发写的场景下,会频繁失败并重试,非常影响性能,导致 CPU 飙升。
- 应用:版本号机制 或 CAS。
悲观锁:认为冲突必然发生,因此在每次操作数据时都会加锁。
- 应用场景:写多读少。可以有效避免冲突,保证数据一致性。
- 应用:synchronized 和 ReentrantLock 都是悲观锁,数据库的SELECT ... FOR UPDATE
2、可重入锁
可重入锁:允许线程多次获取同一把锁。即已经获取锁的线程,在释放锁之前,可以再次获取该锁。
- 解决了在递归调用/内部调用时可能出现的死锁问题
- 应用:synchronized 和 ReentrantLock 都是可重入锁
3、公平锁、非公平锁
公平锁:按照请求锁的顺序,依次分配锁。
- 优:保证了所有线程都能获取到锁,避免饥饿现象。
- 缺:性能开销大,因为需要维护一个等待队列,并进行额外的同步操作,以及上下文切换操作。
- 应用:ReentrantLock可以创建公平锁 new ReentrantLock(true)
非公平锁:不考虑请求顺序,新来的线程可以直接尝试获取锁。
- 优:性能更高,因为可以减少线程上下文切换,新线程获取锁的几率更高。
- 缺:可能导致某些线程一直获取不到锁,造成饥饿。
- 应用:synchronized 和 ReentrantLock(默认)都是非公平锁。
4、读锁、写锁
共享锁:读锁,多个线程可以同时持有锁,用于读操作。但是都不能修改。
- 适用于读多写少
拍他锁:写锁,只有一个线程可以持有锁,执行写操作。此时,读锁和写锁都不能被获取
- 适用于写多读少
应用:数据库中,select for update 读锁; java中,ReentrantReadWriteLock实现了共享锁和排他锁
5、偏向锁、轻量级锁、重量级锁
这三种锁是 JVM用来优化 synchronized 关键字性能的。 synchronized锁会根据竞争情况,经历从无锁 → 偏向锁 → 轻量级锁 → 重量级锁的升级过程。
偏向锁:完全没有并发竞争,只有一个线程在反复进入同步块。
轻量级锁:处理少量并发、短时间竞争,没有获取锁的线程自旋获取锁。
重量级锁:处理高并发、长时间竞争,没有获取锁的线程进入阻塞状态。
锁的升级是单向的,不可逆。
6、自旋锁
一种特殊的锁机制,很少单独使用,通常作为锁性能优化一部分。当线程获取锁失败时,不会进入阻塞,而是循环等待不断尝试获取锁。
- 优:避免了线程上下文切换(从用户态切换到内核态)的开销,在锁竞争时间很短的情况下,性能比重量级锁更好。
- 缺:如果一直无法获取锁,则会不断尝试,浪费 CPU 资源。
应用:比如 synchronized 和 ReentrantLock 在尝试获取锁时,会先尝试自旋一小段时间。
7、版本号机制
通过version字段记录数据被修改的次数。在更新数据值时,读取数据的同时也会读取 version 值,在提交更新时,若当前的 version 值和刚才的version相等才更新,否则重试更新操作。
8、CAS操作
CAS,即比较并交换,是一种无锁编程技术,属于乐观锁。更新数据时,会比较当前的内存值V和预期值A,如果一致则修改内存位置V的值为B,如果不一致说明有线程修改了值,则不做修改。线程不会阻塞,而是循环重试(自旋)直到成功。
- 乐观锁读场景性能好
- 线程不会阻塞,从而减少上下文切换
应用:AtomicInteger, AtomicLong类基于CAS实现 ; Java中实现了CAS操作(Unsafe类实现了compareAndSwapInt),可直接使用
使用场景:轻量的原子操作(如计数器、状态更新)
问题:
1、长时间自旋会消耗cpu资源。
解决:限制自旋次数,如 AtomicInteger 的自旋阈值
2、只能保证一个共享变量的原子操作,多个共享变量时无法保证。
解决:还需要加锁来保证线程安全
3、ABA问题:t2线程先将A改成B,再改回A,此时t1线程以为没人修改过。
解决:在变量前面追加上版本号或者时间戳,修改时进行比较,完全一致才修改。如AtomicStampedReference类就是用来解决 ABA 问题的。
9、synchronized 和 ReentrantLock 有什么区别?
synchronized:关键字
- 悲观锁、非公平锁、可重入锁
- 早期属于重量级锁,在 Java 6 之后, 引入了大量的优化如自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术,性能满足日常使用。
ReentrantLock:java类,需要手动释放
- 悲观锁、非公平锁、可重入锁的基础上,增加高级功能:等待可中断、可实现公平锁、支持超时、可以绑定多个条件。
10、可中断锁和不可中断锁
可中断锁:申请锁之后,等待获取锁的过程中可以被中断,能进行其他逻辑处理。ReentrantLock 就属于是可中断锁。
不可中断锁:一旦线程申请了锁,就只能等到拿到锁以后才能进行其他的逻辑处理。 synchronized 就属于是不可中断锁。

浙公网安备 33010602011771号