ReenTrantLock是juc包下的一个经典的互斥锁,也是 可重入锁 (即当前线程在已经获取改锁后重复执行获取锁操作时不会引起死锁,并且不需要执行获取锁的操作),ReenTrantLock是基于AQS来实现的(PS:注意是基于不是继承,看到网上有的同学会说发现ReentrantLock并没有继承AQS有所疑问),如果有对AQS不了解的同学可以查看我之前的文章 浅析AQS(1)---独占锁以及共享锁的实现 以及 浅析AQS (二)--condition的实现 ,
首先ReenTrantLock是默认的非公平锁,但是也有公平锁的实现,需要在构造方法中传入是否公平的Boolean值来标志,需要注意的是,此处的公平与非公平只针对lock方法,而tryLock方法是指定非公平的.并不受限制
ReenTrantLock实现并发控制是依靠内部类Sync及其两个子类FairSync和NoFairSync,顾名思义这两个子类是提供公平锁以及非公平锁的实现,而Sync则是继承了AQS的具体实现类,接下来我们结合源码具体分析
首先查看一下Sync的源码
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; //抽象lock方法 根据公平与非公平实现执行不同的操作 abstract void lock(); //非公平尝试获取锁 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; } //尝试释放锁 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; } // 是否持有当前锁 protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } //新建condition对象 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; } } 复制代码
在Sync对象中,重写了tryRelease方法,将释放锁的逻辑统一,并且定义了nonfairTryAcquire非公平获取锁的方法,这里为什么要将该方法定义在父类是因为无论是公平还是非公平锁,tryLock方法中都是采用非公平获取的方式,而无论维护的Sync对象时公平的还是非公平的,我们都需要一个可以非公平获取锁的方式
在nonfairTryAcquire我们可以看到,如果当前锁的state为0即当前锁没人持有时则采用cas的方式获取锁,然后将持有现成设置为当前线程,而第二个判断则是可重入锁的实现,即判断持有锁的是不是当前线程,如果是则直接获取锁
关于ConditionObject的实现可以查看我以前的文章 浅析AQS (二)--condition的实现 ,
下面放一下公平与非公平锁的具体实现
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; final void lock() { //尝试获取锁 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } 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; } 复制代码
在NoFairSync中调用lock方法直接尝试用cas的方式获取锁,并不去查看你是否有锁占用,直接尝试获取,如果成功则直接获取锁,如果失败则采用acquire的方式来获取
而在FairSync中实现的tryAcquire方法中与Sync类中的noFairTryAcquire基本一致,只是在没人获取锁时判断当前队列是否为空,或者当前线程是否在队列的最开始位置,总得来说就是判断是否有其他线程等待时长比当前线程要长,主要是为了保证获取锁的公平性
接下来我们看看ReenTrantLock是如何使用这两种锁的,首先来看看构造方法
public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 复制代码
这里指定了锁的公平与非公平,默认采用非公平锁,如需公平锁则传入一个true即可
接下来查看lock与tryLock方法
public void lock() { sync.lock(); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } 复制代码
如同上文所说,在lock方法中是根据当前所公平与非公平来进行而tryLock方法则是固定以非公平的方式来进行调用
以上就是ReenTrantLock的具体实现方式,结合AQS的两篇文章即可将该类理解通透