1,ReentrantLock 的特性
ReentrantLock 是 Java 并发包中提供的一个可重入的互斥锁. ReentrantLock 和 synchronized 在基本用法, 行为语义上都是类似的, 同样都具有可重入性. 只不过相比原生的 Synchronized,ReentrantLock 增加了一些高级的扩展功能, 比如它可以实现公平锁, 同时也可以绑定多个 Conditon.
可重入性:
是指可以支持一个线程对锁的重复获取与释放. 原生的 synchronized 就具有可重入性, 一个用 synchronized 修饰的递归方法, 当线程在执行期间, 它是可以反复获取到锁的, 而不会出现自己把自己锁死的情况. ReentrantLock 也是如此, 在调用 lock() 方法时, 已经获取到锁的线程, 能够再次调用 lock() 方法获取锁而不被阻塞, 并且 lock 和 unlock 的调用次数必须相等才会释放锁.
公平锁 / 非公平锁:
公平锁: 是指线程获取锁的顺序和调用 lock 的顺序一样, FIFO(先进先出) 方式获取和释放锁.
非公平锁: 线程获取锁的顺序和调用 lock 的顺序无关, 能否获取锁取决于调用时机.
synchronized 是非公平锁, ReentrantLock 默认也是非公平的, 但是可以通过带 boolean 参数的构造方法指定使用公平锁, 但非公平锁的性能一般要优于公平锁.
2,ReentrantLock 实现
ReentrantLock 的所有锁相关操作都是通过 Sync 类实现, Sync 继承于 AbstractQueuedSynchronizer 同步队列, 并实现一些通用的接口实现.
NonfairSync 继承于 Sync, 实现了非公平的方式获取锁;
FairSync 继承于 Sync, 实现了公平的方式获取锁;
ReentrantLock 类实现结构如下:
ReentrantLock 类实现结构. PNG
2.1,Sync 实现
- //sync 继承于 AQS
- abstract static class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = -5179523762034025860L;
- // 抽象方法, 需子类实现
- abstract void lock();
- // 实现了非公平方式获取锁
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- // 获取当前锁状态, 此状态 c>0 表示有线程获取到锁, 重入的次数为 c
- int c = getState();
- // 无线程获取锁?
- if (c == 0) {
- //CAS 方式获取锁, 成功则设置获取独占锁的线程
- if (compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- // 获得锁的线程就是当前线程? 则获取次数加 1, 并设置状态 (即次数)
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc <0) // overflow
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
- // 释放锁
- protected final boolean tryRelease(int releases) {
- // 计算需要更新的状态值
- int c = getState() - releases;
- // 若当前线程未获得锁, 则抛出异常
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- // 若将要更新的状态值为 0, 表示当前线程最后一次释放锁;
- // 此时锁才会真正的释放, 其他线程才能获取;
- // 否则表示当前线程获取锁的次数大于释放锁的次数
- if (c == 0) {
- free = true;
- // 清除记录的独占锁的线程
- setExclusiveOwnerThread(null);
- }
- // 更新状态
- setState(c);
- return free;
- }
- // 判断当前线程释放获取独占锁
- protected final boolean isHeldExclusively() {
- // 当前线程为记录的获取独占锁的线程, 则表示当前线程获得独占锁
- return getExclusiveOwnerThread() == Thread.currentThread();
- }
- // 创建新的条件队列
- final ConditionObject newCondition() {
- return new ConditionObject();
- }
- // 获取获取独占锁的线程对象
- final Thread getOwner() {
- return getState() == 0 ? null : getExclusiveOwnerThread();
- }
- // 获取当前线程获取锁的次数
- final int getHoldCount() {
- return isHeldExclusively() ? getState() : 0;
- }
- // 判断锁是否被占用
- final boolean isLocked() {
- return getState() != 0;
- }
- }
2.2, 非公平锁 NonfairSync 的实现
- // 非公平锁 NonfairSync 的实现, 其继承于 Sync
- static final class NonfairSync extends Sync {
- private static final long serialVersionUID = 7316153563782823691L;
- //lock 接口实现;
- // 首先通过 CAS 试图获取锁, 获取成功则设置锁的 Owner;
- // 否则调用 acquire 获取锁, acquire 又或调用 tryAcquire 获取锁,
- // 而 tryAcquire 是通过非公平的方式获取锁.
- final void lock() {
- if (compareAndSetState(0, 1))
- setExclusiveOwnerThread(Thread.currentThread());
- else
- acquire(1);
- }
- // 非公平方式获取锁
- protected final boolean tryAcquire(int acquires) {
- return nonfairTryAcquire(acquires);
- }
- }
2.3, 公平锁 FairSync 的实现
- // 公平锁 FairSync 的实现, 其继承于 Sync
- static final class FairSync extends Sync {
- private static final long serialVersionUID = -3000897897090466540L;
- //lock 接口实现, 自己调用 acquire 获取锁;
- //acquire 又会调用 tryAcquire 获取锁, 而 tryAcquire 是通过公平 (FIFO)
- // 的方式获取锁.
- final void lock() {
- acquire(1);
- }
- // 公平的方式获取锁
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- // 获取当前锁状态, 此状态 c>0 表示有线程获取到锁, 重入的次数为 c
- int c = getState();
- // 无线程获取锁?
- if (c == 0) {
- // 当前节点无前驱节点并且当前线程 CAS 更新状态成功;,
- // 表示当前线程公平的获取到锁
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- // 获得锁的线程就是当前线程? 则获取次数加 1, 并设置状态 (即次数)
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
- }
2.4, 锁可重入的实现原理
通过以上源码分析可知, 当某个节点获取到锁时, 会通过 setExclusiveOwnerThread() 方法记录获取独占锁的线程 Thread; 当某个线程获取锁时, 当锁已被占用, 会判断占用锁的线程是否为当前线程; 是则直接更新锁状态, 表示获取到锁; 否则获取锁失败.
2.5, 锁公平与非公平的实现原理
公平锁是通过 FairSync 实现的, 其在 tryAcquire 获取锁时, 会判断同步队列中当前节点是否有前驱节点; 有前驱节点, 则获取锁失败, 进入同步队列, 等待获取锁; 无前驱节点时, 表示当前节点是同步队列中等待锁时间最长的节点, 则当前节点优先获取锁资源.
非公平锁是通过 NonfairSync 实现的, 其在 lock 及 tryAcquire 时, 会先通过 CAS 的方式尝试获取锁, 获取失败才会进入同步队列等待. 这就导致当某个线程刚释放锁, 而同步队列中被 unpark 的头节点还未 CAS 获取到锁的时间间隙, 当前线程先于同步队列头结点通过 CAS 获取锁. 使得某些线程会等待很长时间才会获得锁, 这是非公平性的.
ReentrantLock 的构造方法如下:
- // 无参构造方法, 会通过非公平的方式实现锁
- public ReentrantLock() {
- sync = new NonfairSync();
- }
- // 带参数的构造方法;
- //fire=true: 会通过公平的方式构造锁;
- //fire=false: 会通过非公平的方式构造锁;
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- }
来源: http://www.jianshu.com/p/120761527e1c