hooyantsing's Blog

0x000A-Java 中的锁概念

2023/08/01

问题详解

640

悲观锁和乐观锁

  • 悲观锁:所以每次操作前都会上锁,其他线程阻塞。在 Java 语言中 synchronized 和 ReentrantLock 等就是典型的悲观锁,还有一些使用了 synchronized 关键字的容器类如 HashTable 等也是悲观锁的应用。适用于读少写多;
  • 乐观锁:乐观锁操作数据时不会上锁,在更新的时候会判断一下在此期间是否有其他线程去更新这个数据。乐观锁可以使用版本号机制和 CAS 算法实现。在 Java 语言中 java.util.concurrent.atomic 包下的原子类就是使用 CAS 乐观锁实现的。适用于读多写少。

独占锁和共享锁

  • 独占锁:是指锁一次只能被一个线程所持有。如果一个线程对数据加上排他锁后,那么其他线程不能再对该数据加任何类型的锁。获得独占锁的线程即能读数据又能修改数据。JDK 中的 synchronized 和 java.util.concurrent(JUC) 包中 Lock 的实现类就是独占锁。
  • 共享锁:是指锁可被多个线程所持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据。在 JDK 中 ReentrantReadWriteLock 就是一种共享锁。

互斥锁和读写锁

  • 互斥锁:是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。
  • 读写锁:是共享锁的一种具体实现,读写锁管理一组锁,一个是只读的锁,一个是写锁。ReentrantReadWriteLock 实现了 ReadWriteLock 接口。

公平锁和非公平锁

  • 公平锁:是指多个线程按照申请锁的顺序来获取锁。
  • 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序。在 java 中 synchronized 关键字是非公平锁,ReentrantLock 默认也是非公平锁。

可重入锁

可重入锁:又称之为递归锁,是指同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁。对于 Java ReentrantLock 而言, 他的名字就可以看出是一个可重入锁。对于 Synchronized 而言,也是一个可重入锁。

自旋锁

自旋锁:是指线程在没有获得锁时不是被直接挂起,而是执行一个忙循环,这个忙循环就是所谓的自旋。因此自旋锁是不适应锁占用时间长的并发情况的。

分段锁

分段锁:是一种锁的设计,并不是具体的一种锁。分段锁设计目的是将锁的粒度进一步细化,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。在 Java 语言中 CurrentHashMap 底层就用了分段锁,使用Segment,就可以进行并发使用了。

锁升级(无锁|偏向锁|轻量级锁|重量级锁)

  • 无锁:无锁状态其实就是上面讲的 乐观锁
  • 偏向锁:Java 偏向锁(Biased Locking) 是指它会偏向于第一个访问锁的线程,如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,那么线程是 不需要重复获取锁 的,这种情况下,就会给线程加一个偏向锁。
  • 轻量级锁:当线程竞争变得比较激烈时,偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过 自旋 方式等待上一个线程释放锁。
  • 重量级锁:升级到重量级锁其实就是 互斥锁 了,一个线程拿到锁,其余线程都会处于阻塞等待状态。

锁优化技术(锁粗化、锁消除)

  • 锁粗化:就是将多个同步块的数量减少,并将单个同步块的作用范围扩大,本质上就是将多次上锁、解锁的请求合并为一次同步请求。
  • 锁消除:是指虚拟机编译器在运行时检测到了共享数据没有竞争的锁,从而将这些锁进行消除。

参考阅读

CATALOG
  1. 1. 问题详解
    1. 1.1. 悲观锁和乐观锁
    2. 1.2. 独占锁和共享锁
    3. 1.3. 互斥锁和读写锁
    4. 1.4. 公平锁和非公平锁
    5. 1.5. 可重入锁
    6. 1.6. 自旋锁
    7. 1.7. 分段锁
    8. 1.8. 锁升级(无锁|偏向锁|轻量级锁|重量级锁)
    9. 1.9. 锁优化技术(锁粗化、锁消除)
  2. 2. 参考阅读