lock()方法
1.入口方法:ReentrantLock.lock()方法.
![](https://img.haomeiwen.com/i14771531/34f6664473972768.png)
主要逻辑交给了内部类FairSync,和 NonfairSync两个类实现。
![](https://img.haomeiwen.com/i14771531/7d4f99395fda0075.png)
![](https://img.haomeiwen.com/i14771531/4e7d54e08243acc5.png)
2.主要分析了公平锁。FairSync.lock()方法。
![](https://img.haomeiwen.com/i14771531/5855ad67b00a35a4.png)
3.AbstracktQueuedSynchronizer.acquire()
![](https://img.haomeiwen.com/i14771531/226c863c1dc2ae79.png)
分为两步:1) 尝试过去锁,如果为false,2) 将不能获取锁的线程,加入队列,并阻塞。
1).FairSync.tryAcquire(1)方法。
![](https://img.haomeiwen.com/i14771531/798ead519db2125f.png)
a)state=0,锁没有被占用,直接则进入if 判断,hasQueuePreecessors,tail==head 或者,tail!=head && (s=h.next)!=null && s.thead == Thread.currentThread();总是,是当前线程是第一个来获取锁的,没有其他线程比他早,就返回false。然后compareAndSetState将状态设为acquires(默认为1),先入队列,然后再比较,实现先入先出,公平锁。在并发获取锁的情况在,在tryAcquire(1),然后大量的线程返回false。再进addWaiter(Node)方法,这是队列就有了数据,这时hasQueuePredecessors()可以快速返回失败,减少更多直接操作内存的风险。
![](https://img.haomeiwen.com/i14771531/8109a5e3f9e22b0a.png)
b)如果是当前线程再次获取锁,就将状态+1,这是在unlock的时候,释放锁-1相互匹配的,直到所有的锁都释放之后,才会让其他线程获得锁。所以,unLock(),必须要放在finally{}中,避免异常时,死锁。
![](https://img.haomeiwen.com/i14771531/e0669ed8765c76a1.png)
另外,private volatile int state; 其中,volatile是ReentrantLock实现内存语义的关键,利用volatile的读写,在释放锁时,reentrantLock锁内的写操作,对后续获取锁的线程可见;
2)加入FIFO队列,先尝试加入到tail中,CAS将节点插入到末尾,然后将原来的tail节点的next只向当前节点。
![](https://img.haomeiwen.com/i14771531/52205b48b5167a9d.png)
否则进入循环,将节点插入到队列中,节点tail节点为null时,说面head和tail都是初始状态。先初始化 tail和head,然后,不断的比对tail节点,尝试将自己设置为tail,直到设置成功以后,才返回出来。
![](https://img.haomeiwen.com/i14771531/0d99c74dfb6ec203.png)
3)循环将队列中的取出来,比对是否是当前队列中最先入队的,否则再次阻塞等待锁的释放。
![](https://img.haomeiwen.com/i14771531/bb7a321eee6e61f8.png)
a)如果当前节点的前节点为head,那么尝试获得锁,将设置当前节点为head,之前的head节点,取消prev和next的引用(设置为null),帮助快速释放GC。
b)获取锁失败进入阻塞状态,shouldParkAfterFailedAcquire(Node pred,Node node),如果前节点状态为SIGNAL,则直接返回,去阻塞(状态分为:CANCELLED =1; SIGNAL= -1;CONDITION = -2;PROPAGATE = -3;默认为0,无状态)。Node节点有两种方式:1,状态控制,2模式控制( Node SHARED =new Node(); Node EXCLUSIVE =null)。公平锁使用模式控制,默认EXCLUSIVE,进入else,将前一个节点的状态设置为SIGNAL,返回false,再下次循环中,如果不能获得锁,则进入阻塞状态。(自旋的一个操作)
![](https://img.haomeiwen.com/i14771531/6c23c90fad55de88.png)
c) 让当前线程阻塞。如果线程重新被唤醒,检查线程是否中断,重置中断状态。如果已经被中断,返回true,如果还没被中断,设置为false,返回false。当线程中断时,设置中断标识为true,在selfInterrupt()中,中断此线程。线程不中断,则进入下一次循环,重新竞争锁。
![](https://img.haomeiwen.com/i14771531/40a44cc3c932ca5a.png)
unlock()方法
![](https://img.haomeiwen.com/i14771531/3f0e9d1fc19d57ed.png)
调用release方法,主要逻辑交给同步类的子类(公平锁和非公平锁),释放资源都由父类AbstractQueuedSynchronizer类来实现。
![](https://img.haomeiwen.com/i14771531/d27b843a31e23702.png)
重点看一下tryRealease
![](https://img.haomeiwen.com/i14771531/eb8fab4b4efa95f9.png)
a) 查看当前占用锁的线程是否为当前线程,否,则抛出异常。
是,则比较C 是否等于0,否则更新状态,状态最多为 Integer.MAX_VALUE,获取锁的最多次数为 Integer.MAX_VALUE。
b)状态为0时,head==null 说明有线程获取锁,还在初始状态,waitStatus==0,说明刚初始化,还没线程获得锁。除了这两种情况,去unpark。
唤醒阻塞的线程。
备注:能看懂和能跟别人讲的懂,还差一段距离,能讲的懂,和能复现还差这一段距离。
网友评论