Java基础回顾:各种锁
参考:
类型
1. 乐观锁 & 悲观锁
乐观锁与悲观锁是一种广义上的概念。
- 悲观锁:对于同一个数据,悲观锁认为线程在使用数据时,总有其他线程来修改数据,因此在获取数据的时候回先加锁,确保数据不会被其他线程修改。java中Synchronized关键字和Lock接口实现类都是悲观锁。如下:
- 乐观锁:乐观锁认为自己在使用数据时不会有其他线程来修改数据,所以不会添加锁。指示在更新数据的时候去判断数据是否有被其他线程更新。如果这个数据没被更新,则当前线程获取数据并更新,如果数据已经被其他线程修改,则根据不同的实现方式执行不同的操作(例如报错或自动重试)。乐观锁在Java中是通过无锁编程来实现,最常采用的CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。
- CAS:一种无锁算法,Compare And Swap(比较交换)。
- 需要读写的内存位置V
- 需要进行比较的预期值A
- 需要写入的新值U
CAS 存在问题:
ABA问题(加版本号解决),循环时间长开销大,只能保证一个共享变量 的原子操作。
悲观锁是和写操作多的场景,先加锁可以保证数据正确性;
乐观锁适合读操作多的场景,不加锁的特性可以使读操作的性能大大提升。
2. 自旋锁 & 适应性自旋锁
自旋锁实现原理:CAS
例:AtomicInteger自增操作
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013223850835-588531575.png)
适应性自旋锁:自旋的次数不再固定,由前一次在同一个锁上的自旋时间及锁的拥有者状态来决定。
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013223911555-976132348.png)
3. 无锁 & 偏向锁 & 轻量级锁 & 重量级锁
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013223923950-477978963.png)
无锁:没有对资源进行锁定,所有线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,实现:CAS
偏向锁:指一段同步代码一直被同一个线程访问,那么该线程会自动获取锁,降低获取锁的代价。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。
jdk6以后默认开启 JVM参数 -XX:-UseBiasedLocking=false
轻量级锁:当锁是偏向锁时,被其他线程访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的方式获取锁,不会阻塞,从而提高性能。CAS+自旋
重量级锁:若当前只有一个等待线程,则该线程通过自旋进行等待。但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013224013262-1551866455.png)
4. 公平锁 & 非公平锁
公平锁:多个线程按照申请锁的顺序来获取锁,线程在队列中排队,队列中第一个线程获取锁。线程不会饿死,但是效率低。
非公平锁:线程直接尝试去获取锁,获取不到才进入队列等待。效率高,但是线程有可能饿死。
例:ReentrantLock有一个内部类Sync,Sync继承AQS(AbstractQueuedSybchronizer),加锁和释放锁多事类Sync实现,有公平锁(FairSync extends Sync)和非公平锁(NonfairSync extends Sync),默认使用非公平锁。
![](https://img2020.cnblogs.com/blog/1461236/202110/1461236-20211013224041888-2126043059.png)
5. 可重入锁 & 非可重入锁
可重入锁:同一个线程可以重复获取锁,获取一次锁status 加1,释放一次锁status 减1,可重入锁的一个优点是可一定程度避免死锁。ReentrantLock和Synchronized都是可重入锁。
非可重入锁:NonReentrantLock
6. 独享锁 & 共享锁
独享锁:排它锁,该锁只能被一个线程持有。
共享锁:可以被多个线程持有。
一点浩然气,千里快哉风!