ReentrantLock 源码分析 -- ReentrantLock 的加锁与解锁

2018-08-13 15:33:48   最后更新: 2018-08-13 17:30:24   访问数量:105




上一篇日志中,我们介绍了 ReentrantLock 的用法,他就是通过 AQS 来实现的,也是 AQS 独占模式的典型示例

ReentrantLock 用法详解

AQS (Abstract Queued Synchronizer)源码解析 -- 独占锁与共享锁的加锁与解锁

接下来我们就来看看 ReentrantLock 是如何实现的

 

 

根据类图,我们可以看到 ReentrantLock 拥有三个内部类,分别是

上文类图中,Sync 实现了解锁的 tryRelease 与 tryReleaseShared 方法,而其子类 NonfairSync(非公平锁) 与 FairSync(公平锁) 则实现了 tryAcquire 与 tryAcquireShared 及 isHeldExclusively 方法,从而让整个锁的实现层次更加清晰

 

上文中我们介绍过 ReentrantLock 的加锁与解锁方法,我们来看看他们究竟是怎么实现的

 

加锁

  • 加锁 -- lock
public void lock() { sync.lock(); }

 

 

  • 可中断锁 -- lockInterruptibly
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }

 

 

  • 尝试获取锁 -- tryLock
public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }

 

 

解锁 -- unlock

public void unlock() { sync.release(1); }

 

 

可以看到,所有的加锁解锁方法均直接调用了内部类 Sync

 

根据类图,我们可以看到,ReentrantLock 拥有一个内部类 Sync,他是 AQS 的子类,同时,ReentrantLock 还拥有另外两个内部类 -- FairSync 和 NonfairSync,他们分别提供了公平锁与非公平锁的加锁方法

 

公平锁是通过 Sync 的子类 FairSync 实现的,他复写了父类的 abstract lock 方法

static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }

 

 

lock 方法直接调用了 AQS 的 acquire 方法,这个方法在之前的文章中已经介绍过了,这里不再赘述

同时,他实现了 Sync 没有实现的 tryAcquire 方法,这个方法就是实际上的公平锁的加锁方法

  1. 此前我们看到,AQS 中有一个 state 成员,这个 int 类型的成员就是锁状态的标识,为 0 则表示当前锁处于空闲状态,否则说明锁处于非空闲状态
  2. 同时我们提到过,AbstractQueuedSynchronizer 继承自 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 中 exclusiveOwnerThread 字段记录了当前正在占用锁的线程

了解了上述两点,公平锁的加锁代码就非常简单了

 

 

通过 AbstractQueuedSynchronizer 提供的原子操作 compareAndSetState 实现了加锁的并发控制,当然他最终调用的是 UNSAFE 类的成员方法:

protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }

 

 

NonfairSync 类提供了非公平锁的加锁方法

static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }

 

 

可以看到,非公平锁的加锁与公平锁的加锁有一点不同,那就是如果当前锁状态是空闲状态,那么就可以直接获取锁,而公平锁显然是不能这么做的,因为公平锁必须要先判断 AQS 同步队列中是否有先入队的正在等待获取锁的线程,否则直接获取就打破了公平锁的原则了

同样的,真正的加锁方法仍然是 Sync 所没有实现的 tryAcquire 方法,NonfairSync 中直接调用了父类的 nonfairTryAcquire 方法,也就是说,虽然 Sync 并没有直接实现 tryAcquire 方法,但他其实已经提供了一个非公平锁的加锁实现,那就是 nonfairTryAcquire 方法,我们来看看 Sync 的 nonfairTryAcquire 方法做了什么

 

非公平锁的加锁 -- Sync.nonfairTryAcquire

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } 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; }

 

乍一看,这个方法和我们上面介绍过的公平锁加锁方法似乎没什么区别,仔细一看,还是我们上面讲到的,非公平锁与公平锁最大的区别在于,他在获取锁之前不需要先去判断 AQS 同步队列中是否有正在等待获取锁的节点,这样,非公平锁的实现看起来就更简单了

 

 

protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }

 

这段解锁的代码看上去非常简单,既然加锁的时候对 AQS 的状态值进行了增加操作,那么在解锁的时候就必须进行逆向的减操作了,同时,如果此时锁已经被释放,那么就需要把 AbstractOwnableSynchronizer 类中的 exclusiveOwnerThread 成员置空

 






技术帖      技术分享      多线程      java      原子操作      加锁      解锁      高并发      并发安全      reentrantlock     


京ICP备15018585号