可重入锁ReentrantLock的底层是通过AbstractQueuedSynchronizer实现。
带着BAT大厂的面试问题去理解
什么是可重入,什么是可重入锁? 它用来解决什么问题?
ReentrantLock的核心是AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。
ReentrantLock是如何实现公平锁的?
ReentrantLock是如何实现非公平锁的?
ReentrantLock默认实现的是公平还是非公平锁?
使用ReentrantLock实现公平和非公平锁的示例?
ReentrantLock和Synchronized的对比?
ReentrantLock源码分析 类的继承关系 ReentrantLock实现了Lock接口,Lock接口中定义了lock与unlock相关操作,并且还存在newCondition方法,表示生成一个条件。
1 2 3 4 5 6 7 8 9 public class ReentrantLock implements Lock , java .io .Serializable { abstract static class Sync extends AbstractQueuedSynchronizer {} static final class NonfairSync extends Sync {} static final class FairSync extends Sync {} }
内部类 ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。
ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。
Sync类 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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 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(); 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 ) 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(); } 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 ); } }
NonfairSync类 NonfairSync类继承了Sync类,表示采用非公平策略获取锁,其实现了Sync类中抽象的lock方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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); } }
从NonfairSync#lock方法 的源码可知,lock时先compareAndSetState(0, 1)
尝试获取锁,而并不会按照公平等待的原则进行等待,让sync queue中的第一个线程获得锁。
FairSyn类 FairSync类也继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中的抽象lock方法。
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 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 ; } }
只要资源被其他线程占用,FairSyn 会将该线程添加到sync queue中的尾部,而不会先尝试获取资源。 这也是和Nonfair 最大的区别,Nonfair每一次都会尝试去compareAndSetState获取资源(有两次机会),如果此时该资源恰好被释放,则会被当前线程获取,这就造成了不公平的现象 ,当获取不成功,再加入队列尾部。
公平锁下,同步队列Sync queue的头节点线程一定可以拿到资源吗? 理论上,同步队列的头节点线程应该是第一个获取到锁的线程。但是并非头节点线程一定可以获取到锁。 公平锁只是尽最大努力保证同步队列的线程可以按顺序获得锁,但终究还是受限于系统调度和锁的实际使用状态。
类的属性 1 2 3 4 5 6 public class ReentrantLock implements Lock , java .io .Serializable { private static final long serialVersionUID = 7373984872572414699L ; private final Sync sync; }
ReentrantLock类的sync非常重要,对ReentrantLock类的操作大部分都直接转化为对Sync和AbstractQueuedSynchronizer类的操作。
构造函数
ReentrantLock() 1 2 3 4 public ReentrantLock () { sync = new NonfairSync(); }
ReentrantLock(boolean fair) 可以传递参数确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则,采用非公平策略。 1 2 3 public ReentrantLock (boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
核心方法 通过分析ReentrantLock的源码,可知对其操作都转化为对Sync对象的操作,由于Sync继承了AQS,所以基本上都可以转化为对AQS的操作。 如将ReentrantLock的lock函数转化为对Sync的lock函数的调用,而具体会根据采用的策略(如公平策略或者非公平策略)的不同而调用到Sync的不同子类。 所以可知,在ReentrantLock的背后,是AQS对其服务提供了支持。
示例分析 可重入的例子 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 public class ReentrantLockExample { private ReentrantLock lock = new ReentrantLock(); public void method1 () { lock.lock(); try { method2(); } finally { lock.unlock(); } } public void method2 () { lock.lock(); try { } finally { lock.unlock(); } } public static void main (String[] args) { ReentrantLockExample example = new ReentrantLockExample(); example.method1(); } }