ReentrantLock
简介ReentrantLock
是可重入锁的实现,可重入锁的含义是:如果已经拥有锁的线程再次获取锁时会立即响应成功,这点可以使用isHeldByCurrentThread
和getHoldCount
方法来检验。ReentrantLock
可重入互斥锁具有同synchronized
的隐式监视器锁相同的基本行为和语义,但是其更具有扩展能力。
ReentrantLock
提供公平锁和非公平锁的特性,如果设置为公平锁,那么锁倾向于访问等待时间最长的线程;如果是非公平锁,那便不会保证任何特定的访问顺序。公平锁会导致整体的吞吐量降低,不过却可以在最少的时间差内获取到锁和保证锁饥饿。不过请注意,锁是公平并不能保证线程调度的公平性。
ReentrantLock
使用典型用法如下(这里以lock
为例,当然也可以用tryLock
方法来获取锁):
1 2 3 4 5 6 7 8 9 10 11 12 13 class X { private final ReentrantLock lock = new ReentrantLock(); public void m () { lock.lock(); try { } finally { lock.unlock() } } }
ReentrantLock
源码剖析源码中涉及AQS模板类的调用,可选择先查看《JUC之AQS解读》 这篇文章的介绍。
同步器 对于同步状态,依然是继承与AQS来实现的,考虑到有公平锁和非公平锁的特性,其又做了一层抽象,提供一些共用方法,如:
对于lock
的加锁过程则交予具体的锁类型同步器去实现
获取非公平锁: nonfairTryAcquire
释放锁:tryRelease
是否当前线程持有锁:isHeldExclusively
条件对象:newCondition
对象序列化:readObject
线程持锁状态:getOwner,getHoldCount,isLocked等
其中获取非公平锁方式放在这个抽象层的原因是为了ReentrantLock
在默认非公平性下调用:
1 2 3 public boolean tryLock () { return sync.nonfairTryAcquire(1 ); }
抽象同步器的源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 abstract static class Sync extends AbstractQueuedSynchronizer { abstract void lock () ; final boolean nonfairTryAcquire (int acquires) { } protected final boolean tryRelease (int releases) { } 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 ; } private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0 ); } }
公平锁与非公平锁 ReentrantLock
公平锁和非公平锁的特性可以通过选择如下构造函数来设定:
1 2 3 public ReentrantLock (boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
如果直接使用默认的无参构造函数,那么是用的非公平锁:
1 2 3 public ReentrantLock () { sync = new NonfairSync(); }
公平锁和非公平锁的同步状态器源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 static final class NonfairSync extends Sync { final void lock () { if (compareAndSetState(0 , 1 )) setExclusiveOwnerThreadcurrentThread(Thread.()); else acquire(1 ); } protected final boolean tryAcquire (int acquires) { return nonfairTryAcquire(acquires); } } static final class FairSync extends Sync { 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 ; } }
同时结合前面的抽象同步器代码,可以总结出区别在于:
非公平的加锁在最开始阶段会进行一次资源竞争抢占compareAndSetState(0, 1)
,如果抢占成功则直接加上锁。
公平性加锁在尝试获取锁的时候,如果state==0
的条件下多了一层判断!hasQueuedPredecessors()
来保证公平有序。
hasQueuedPredecessors
的作用是判断是否有其他线程先于当前线程进行等待,避免非公平抢占。其源码如下,通过判断是否有等待节点来告知:
1 2 3 4 5 6 7 public final boolean hasQueuedPredecessors () { Node t = tail; Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
可重入性 对于可重入性,就是在判断到当前线程持有锁的时候,再将状态值进行加法:
1 2 3 4 5 6 7 else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0 ) throw new Error("Maximum lock count exceeded" ); setState(nextc); return true ; }
对于可重入的次数而言,由于继承的是AQS并使用其state
字段,所以最大值只能到Integer.MAX_VALUE
,超出此限制会抛出Error
。
1 2 3 4 private volatile int state;
锁释放 回到抽象同步器的tryRelease
方法,这是对锁释放的一个操作,其完成的功能有如下3步:
判断是否是当前线程,不是则抛异常;
释放状态值:state - releases
;
如果状态值为0,则释放排他锁;
设置状态值并返回;
1 2 3 4 5 6 7 8 9 10 11 12 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模板类的使用,会根据如下调用流程走完锁释放流程:
1 ReentrantLock.unlock -> aqs.release -> ReentrantLock.tryRelease