同步
在ReentrantLock的类中,它定义了三个内部类Sync、NonfairSync、FairSync,从名称看,这是ReentrantLock支持公平锁、非公平锁的支持类。
从类的继承关系看,他们都继承了AQS。而,从具体实现看NonfairSync、FairSync都是继承于Sync,而不是直接继承AQS;所以,Sync应该封装了适用于ReentrantLock的同步器通用逻辑。且,有一个属性为Sync。
同步的封装表现
锁的表现
ReentrantLock实现Lock类,并实现了对应的方式。以Lock的方式表达了ReentrantLock的锁的语义,并以Lock的通用接口,来让整体使用都是统一的。
当然,整体都是使用了同步器的 Sync 属性的对应方法去具体实现。
Monitor-Style实现
从注释看,ReentrantLock的Monitor-Style的同步实现是基于实现了AQS的Sync获取Condition对象,基于Condition对象的await与signal的方式来实现的。
同步器的Sync深度解析
主要代码总览
从代码上看Sync类,实现了如下的几个方法,nonfairTryAcquire和tryRelease
在NonFairSync类中,只实现了lock与tryAcquire方法,其中tryAcquire直接调用了从Sync继承下来的nonfairTryAcquire方法。release方法则直接继承自Sync类。
在FairSync类中,则实现了lock与tryAcquire方法。release方法则直接继承自Sync类
在公平锁与非公平锁的角度
通过公平锁的定义知道,先进来的线程先获取到锁。
从非公平锁的定义知道,获取锁时,刚好没有任何线程竞争过我,则由快的获取到。
通过上面的总览知道,NonFairLock继承自Sync的nonfairTryAcquire是非公平锁的具体实现。
而,FairLock的公平锁具体实现tryAcquire方法。
对比时,我们只看下面俩段。
从实现上看,其步骤都会获取锁的状态,为0时,则是可以获取的时候。进入到线程获取锁的判断。
非公平锁的实现:
步骤一:先通过compareAndSetState这个CAS动作直接修改锁的状态(由CAS已知其保证了多线程竞争时的原子性,能保证只有一个线程可以获取到)
步骤二:在通过setExclusiveOwnerThread方法,设置了该线程为最终得主。
公平锁实现:
步骤一:判断队列中是否还有前驱,如果有则失败,没有则进入步骤二
步骤二:跟非公平锁一样的实现。
对比非公平与公平锁的实现,可以知道ReentrantLock的公平锁与非公平锁的实现,就是判断队列中是否有前驱,采取竞争锁。
其实:Doug Lea的《The java.util.concurrent Synchronizer Framework》文章中,有一句这样的话,就说明了其实现。
在可重入实现的角度
在可重入的角度,从可重入的定义知道,一个线程可以一直获取已经属于它的锁。
那么,只要做的俩点,就能实现这个定义
- 获取锁的线程,判断是否是已经获取到锁的线程
- 修改获取到锁的状态,且能在释放锁的时候,正确维护这个状态,重新设置为可以获取锁的状态
从上面公平锁与非公平锁的实现中,我们知道了有一个方法setExclusiveOwnerThread可以设置现在那个线程正拥有锁。所以解决了问题1。
现在,我们来看下面几个代码片段,如何维护锁的状态,正确表示锁的重入,以及释放。
重入状态变更
释放状态变更
从重入状态变更中看,无论是公平锁还是非公平锁,都实现了一块代码
步骤一:判断是否为当前线程获取到锁,如果是进入步骤二
步骤二:将当前的状态值,再加上一次acquires的值。并设置为新的状态值
从释放状态变更中看,由于NonFairSync与FairSync都是继承自Sync的tryRealase方法。所以,释放用的就是相同的代码。
步骤一:将状态值,减少一个release值
步骤二:判断是否是为当前线程获取到锁,如果是进入步骤三
步骤三:如果状态值已经为0,释放锁。若不是,则将设置新的状态值(步骤一所得)。
从上面的分析可以知道,重入是加上一个值,释放是减少一个值。如果,我们tryAcquire与tryRelease时用的是同一个字的话,那么最终执行相同个数的tryAcquire与tryRelease,可以保证锁的状态重新回到可以获取的状态(值为0)。从下方ReentrantLock的俩个实现tryLock与unlock都是封装了tryAcquire与tryRelease中的实现,而其acquires与arg值都为1(这里之后再深挖进去,内部使用了tryRelease方法),验证了我们的想法。
故,这里解决了问题2.
所以,这就是ReentrantLock可重入的实现。其实这里跟Synchronized关键字的实现思路基本一致,而Synchronized的实现是基于MonitorEnter与MonitorExit指令。
Monitor-Style的实现