锁
1. 锁分类
-
乐观锁
- 认为当前线程修改“共享内存数据”时,应该不会有其他线程去修改同一份数据
- 所以操作过程“不会加锁”
- 例子
- CAS——比较并变换
- 比较 内存中的数据值 和 期望的值 是否相同,只有相同时才会更新
- 需要依赖底层 CPU 的 cmpxchg 指令,因为要把比较与更新绑定为一整个原子操作,防止中途被分片切走导致判断过时
- CAS——比较并变换
- 认为当前线程修改“共享内存数据”时,应该不会有其他线程去修改同一份数据
-
悲观锁
- 认为当前线程修改“共享内存数据”时,一定会有其他线程来修改
- 所以操作过程“必须先加锁”
- 例子
- Java 中的 Sychronized 和 ReentrantLock
- 认为当前线程修改“共享内存数据”时,一定会有其他线程来修改
-
公平锁
- 如果当前锁已经被某个线程持有,那新来的线程就要去排队
-
非公平锁
- 如果当前锁已经被某个线程持有,那新来的线程可以先用 CAS 抢一下,抢到了就能往下执行,抢不到再去排队
- ReentrantLock 默认是非公平锁,但可以改为公平锁
-
重入锁
- 如果一个线程已经获取到了锁,那这个线程下次进入同步代码时,可以直接进入,不用重新获取锁
- sychronized 和 ReentrantLock 都是可重入锁
-
非重入锁
- 即不可重入的锁
-
独占锁
- 如果一个线程已经获取到了锁,那其他线程不可以继续获取锁
-
共享锁
- 即不独占的锁
2. 锁升级
无锁->偏向锁->轻量级锁->重量级锁
-
无锁
- 即不加锁
- 乐观锁的 CAS 就是一个例子
-
偏向锁
- 如果一个线程获取锁后,执行过程中一直没有其他线程来竞争锁,那可以让其持有一个偏向锁
- 持有偏向锁的线程在执行时,就不再需要获取锁
- 从而减少锁的开销
-
轻量级锁
- 如果一个线程想要竞争的锁很快就会被释放,那可以让其使用轻量级锁
- 想获取轻量级锁的线程在竞争时,如果暂时获取不到,就会自旋一段时间,等待锁释放
- 从而减少挂起唤醒和 CPU 上下文切换的开销
-
重量级锁
- 即一般的锁
- 如果一个线程获取不到锁,就会被挂起,等待锁释放后被唤醒