ReentrantLock 源码解析

1、个人总结和看法:

(1)、AQS和ReentrantLock的关系?

ReentrantLock是基于AQS的实现的,昨天我们说了AQS的tryAcquire()是默认抛出异常的需要子类去重写逻辑,ReentrantLock就重写了tryAcquire()。这样就解释了之前的疑问,因为这本来就是留给子类自己去完成的逻辑。

(2)、ReentrantLock的锁模式?

默认是非公平获取锁,不过可以在构造是设置公平锁模式获取。

(3)、为什么ReentrantLock的锁模式默认为非公平锁?

我的理解是当在竞争锁时,如果为非公平锁模式,那么当有新开启的线程在和队列中的线程竞争时,新开启的线程会更容易获取锁,因为队列唤醒需要更多操作,这样的避免了入队和队列唤醒的操作开销,节约了很多的性能,并发原理分析始终要从安全性和性能考虑。

 

2、内部结构解析:

(1)、ReentrantLock有一个内部抽象类 Sync,有一个lock抽象方法,它继承了AQS也就解释了ReentrantLock和AQS的关系。还有两个抽象类FairSync和NonfairSync 他们都继承了Sync,重写了lock方法,分别为公平和非公平获取锁的实际处理逻辑。

(2)、构造方法

1 public ReentrantLock() {
2         //默认为非公平获取锁
3         sync = new NonfairSync();
4     }
1 public ReentrantLock(boolean fair) {
2 //也可以传入参数为true就表示公平锁
3         sync = fair ? new FairSync() : new NonfairSync();
4     }

 

3、重点方法源码分析:

(1)、非公平锁获取

 1 static final class NonfairSync extends Sync {
 2         private static final long serialVersionUID = 7316153563782823691L;
 3 
 4         final void lock() {
 5 //首先设置状态位 
 6             if (compareAndSetState(0, 1))
 7 //如果设置状态位成功 就设置独占线程
 8                 setExclusiveOwnerThread(Thread.currentThread());
 9             else
10 //失败后竞争锁
11                 acquire(1);
12         }
13 
14         protected final boolean tryAcquire(int acquires) {
15 //非公平锁获取锁的实际逻辑
16             return nonfairTryAcquire(acquires);
17         }
18     }

总结:所以你能看到其实ReentrantLock当设置独占线程失败后是直接调用的AQS中的方法,进行入队和其他操作。这里应该将ReentrantLock和AQS的关系体现的比较明显了,重点是看ReentrantLock重写的tryAcquire方法。

 

acquire() 源码分析:

1 public final void acquire(int arg) {
2        //这里我们昨天分析过了  就是AQS的方法 
3         if (!tryAcquire(arg) &&
4             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
5             selfInterrupt();
6     }

这里其实就是AQS的方法。

 

nofairTryAcquire()方法源码分析:

 1 final boolean nonfairTryAcquire(int acquires) {
 2           //获取当前线程
 3             final Thread current = Thread.currentThread();
 4 //获取状态位
 5             int c = getState();
 6             if (c == 0) {
 7 //如果没有被设置 就设置 并设置独占线程为自己
 8                 if (compareAndSetState(0, acquires)) {
 9                     setExclusiveOwnerThread(current);
10                     return true;
11                 }
12             }
13 //如果设置了  就检查独占线程是不是自己  是自己的话那就再次获取 这就是可重入锁
14             else if (current == getExclusiveOwnerThread()) {
15                 int nextc = c + acquires;
16                 if (nextc < 0) // overflow
17                     throw new Error("Maximum lock count exceeded");
18                 setState(nextc);
19                 return true;
20             }
21 //否则返回false
22             return false;
23         }

这里我们的非公平方式获取锁的逻辑就明白了  所以调用流程其实是(假设存在竞争,并且失败,因为这个流程比较复杂,其他相对简单)lock()、acquire()、tryAcquire()、nofairTryAcquire()、addWaiter()(其他是AQS参看AQS个人分析)

所以ReentrantLock其实是在AQS的基础上完成的,自己也就是设置独占线程这种简单操作,当遇到竞争时操作都是AQS的逻辑完成。

 

(2)、非公平锁的释放

释放相对于简单一些,昨天我们并没有分析AQS的释放 所以我们现在分析一下,直接上源码

我还是喜欢根据逻辑来分析 这样也方便大家分析

unLock()源码分析:

1 public void unlock() {
2 //实际上是Sync的释放  再向下转型到非公平锁
3         sync.release(1);
4     }

 

 

release()源码分析:

 1 //这是AQS中的方法了
 2 public final boolean release(int arg) {
 3 
 4 //尝试释放  我们应该能猜到tryRelease是子类实现的
 5         if (tryRelease(arg)) {
 6 //获取头节点
 7             Node h = head;
 8 //如果不为空且不为等待状态
 9             if (h != null && h.waitStatus != 0)
10 //释放头节点
11                 unparkSuccessor(h);
12 释放成功返回true
13             return true;
14         }
15         return false;
16     }

 

tryRelease()分析:

 1  protected final boolean tryRelease(int releases) {
 2 //获取状态位  
 3             int c = getState() - releases;
 4 //如果当前线程不是持有锁的线程就抛出异常
 5             if (Thread.currentThread() != getExclusiveOwnerThread())
 6                 throw new IllegalMonitorStateException();
 7             boolean free = false;
 8 //如果状态位为0  则表示已经成功的释放了锁 
 9             if (c == 0) {
10                 free = true;
11 //设置独占线程为null
12                 setExclusiveOwnerThread(null);
13             }
14 //否则返回当前状态位 
15             setState(c);
16             return free;
17         }

 

 其实ReentrantLock是通过一个volatile的state来表示当前所的状态,其实就是内部规定的机制而已,这就是抽象意义的锁。就是所有权。

 

 (3)、公平锁获取:

因为和非公平锁的性质类似 我们主要分析不同的地方,直接上源码吧。

 1 static final class FairSync extends Sync {
 2         private static final long serialVersionUID = -3000897897090466540L;
 3 
 4         final void lock() {
 5 //重写了lock方法
 6             acquire(1);
 7         }
 8 
 9      
10         protected final boolean tryAcquire(int acquires) {
11 //获取当前线程
12             final Thread current = Thread.currentThread();
13             int c = getState();
14             if (c == 0) {
15 //这里和非公平不同的是,它就算知道锁没有被占有,还是会去检查自己前面还有没有等待的线程  公平锁嘛  必须要前面没有线程等待了 才会去获取锁
16                 if (!hasQueuedPredecessors() &&
17                     compareAndSetState(0, acquires)) {
18                     setExclusiveOwnerThread(current);
19                     return true;
20                 }
21             }
22             else if (current == getExclusiveOwnerThread()) {
23                 int nextc = c + acquires;
24                 if (nextc < 0)
25                     throw new Error("Maximum lock count exceeded");
26                 setState(nextc);
27                 return true;
28             }
29             return false;
30         }
31     }

 

请注意我在代码注释里面说的和非公平锁的不同点 这是重点

我们看看hasQueueedPredecessors()的源代码吧:

 

1 public final boolean hasQueuedPredecessors() {
2        //从代码里面很容易看到逻辑是
3 //头节点初始化了 并且当前节点是第一节点
4         Node t = tail; // Read fields in reverse initialization order
5         Node h = head;
6         Node s;
7         return h != t &&
8             ((s = h.next) == null || s.thread != Thread.currentThread());
9     }

 

 

 

释放节点就不分析了 差不多

posted @ 2018-02-26 11:45  Chaer  阅读(153)  评论(0编辑  收藏  举报