目录
1.常见的锁策略
1.乐观锁 vs 悲观锁
2.读写锁
3.重量级锁 vs 轻量级锁
4.自旋锁(Spin Lock)
5.公平锁 vs 非公平锁
6.可重入锁 vs 不可重入锁
7.Synchronized实现了哪些锁策略?
1.是乐观锁也是悲观锁
2.既是轻量级锁也是重量级锁
3.是普通互斥锁
4.是非公平锁
5.是可重入锁
6.既是自旋锁也是挂起等待锁
8.CAS(Compare And Swap)
9.基于CAS的应用,原子类
10.CAS实现自旋锁
11.CAS的ABA问题
1.ABA问题
2.解决ABA问题
12.加锁工作过程
13.锁消除
14.锁粗化
1.常见的锁策略
1.乐观锁 vs 悲观锁
- 悲观锁: 一开始就会上锁
- 乐观锁: 假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何去做
2.读写锁
- 多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方互相之间以及和读者之间都需 要进行互斥。如果两种场景下都用同一个锁,就会产生极大的性能损耗。所以读写锁因此而产生。
- 读写锁(readers-writer lock),看英文可以顾名思义,在执行加锁操作时需要额外表明读写意图,复数读者之间并不互斥,而写者则要求与任何人互斥。
- ReentrantReadWriteLock.ReadLock 类表示一个读锁. 这个对象提供了 lock / unlock 方法进行加锁解锁.
- ReentrantReadWriteLock.WriteLock 类表示一个写锁. 这个对象也提供了 lock / unlock 方法进行加锁解锁.
- 读加锁和读加锁之间, 不互斥.
- 写加锁和写加锁之间, 互斥.
- 读加锁和写加锁之间, 互斥.
3.重量级锁 vs 轻量级锁
- CPU 提供了 "原子操作指令".
- 操作系统基于 CPU 的原子指令, 实现了 mutex 互斥锁.
- JVM 基于操作系统提供的互斥锁, 实现了 synchronized 和 ReentrantLock 等关键字和类.
- 大量的内核态用户态切换
- 很容易引发线程的调度
重量级锁过程
- 执行加锁操作, 先进入内核态.
- 在内核态判定当前锁是否已经被占用
- 如果该锁没有占用, 则加锁成功, 并切换回用户态.
- 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.
- 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒 这个线程, 尝试重新获取锁.
- 少量的内核态用户态切换.
- 不太容易引发线程调度
- 通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)
- 如果更新成功, 则认为加锁成功
- 如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU).
4.自旋锁(Spin Lock)
- 如果获取锁失败, 立即再尝试获取锁, 无限循环, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来.
- 自旋锁是一种典型的 轻量级锁 的实现方式.
- 优点: 没有放弃 CPU, 不涉及线程阻塞和调度, 一旦锁被释放, 就能第一时间获取到锁.
- 缺点: 如果锁被其他线程持有的时间比较久, 那么就会持续的消耗 CPU 资源. (而挂起等待的时候是不消耗 CPU 的).
5.公平锁 vs 非公平锁
- 公平锁: 遵守 "先来后到". B 比 C 先来的. 当 A 释放锁的之后, B 就能先于 C 获取到锁.
- 非公平锁: 不遵守 "先来后到". B 和 C 都有可能获取到锁.
- 操作系统内部的线程调度就可以视为是随机的. 如果不做任何额外的限制, 锁就是非公平锁. 如果要 想实现公平锁, 就需要依赖额外的数据结构, 来记录线程们的先后顺序.
- 公平锁和非公平锁没有好坏之分, 关键还是看适用场景.
6.可重入锁 vs 不可重入锁
- Java里只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括
- synchronized关键字锁都是可重入的。
- 而 Linux 系统提供的 mutex 是不可重入锁.
7.Synchronized实现了哪些锁策略?
1.是乐观锁也是悲观锁
开始没有线程竞争的时候无锁,当有第一个线程来使用的时候加偏向锁,偏向锁并不是真正的加锁,只是给对象头做一个偏向锁的标记,记录这个锁属于哪个线程,如果后续没有其他线程来争抢锁,那就不用真正的加锁,(避免了加锁解锁的系统开销)并记录当前线程的版本号,当出现两个以上的线程竞争时变为轻量级锁,当越来越多的线程参与到竞争中,自旋不能快速获取到锁状态,此时就会变为重量级锁
2.既是轻量级锁也是重量级锁
轻量级锁是基于自旋锁实现的,重量级锁是基于挂起等待锁实现的
3.是普通互斥锁
4.是非公平锁
5.是可重入锁
6.既是自旋锁也是挂起等待锁
8.CAS(Compare And Swap)
9.基于CAS的应用,原子类
两个线程通过CAS同时对一个共享变量做自增,通过不停的自旋检查预期值来保证了线程安全
while循环是在应用层执行的,也就是用户态锁比内核态的锁效率要高很多
Compare And Swap 是CPU中的一条指令,可以完成CAS的整个操作(比较并交换),简而言之,是因为硬件予以了支持,软件层面才能做到
10.CAS实现自旋锁
11.CAS的ABA问题
1.ABA问题
在上述过程中,两个A校验的时候都可以通过,但两个A却并非同一个A,
如果CAS中出现ABA问题,在真实的业务中可能会造成比较大的影响
2.解决ABA问题
给预期值加一个版本号,在做CAS操作的同时更新预期值的版本号,版本号只增不减
CAS 操作在读取旧值的同时, 也要读取版本号,真正修改的时候
- 如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 + 1.
- 如果当前版本号高于读到的版本号. 就操作失败(认为数据已经被修改过了)