解读源码本来就是一件极其枯燥乏味的事情 希望你坚持住 过了这道坎 你会看到不一样的风景;
在解读源码之前我们来讨论一下源码解读的技巧:
1、跑不起来的源码不读
因为大部分源码都会用到好多的设计模式,这就促使如果源码跑不起来,单纯的看是很难看的懂,如果可以跑起来你就可以Ctrl鼠标单击方法跟进去,这样就会事半功倍;
2、解决问题就好-目的性
在实际工作中你可能会接手一个改过不知道多少遍的代码,这时你就要搞清楚,如果单纯的只是为解决问题,就没有必要去看细节;
3、一条线索到底
读源码一定要一条线跟到底,不要只是都表面,我们直到当一个程序跑起来,可能会很大,很多方法点进去还会调用其他的方法,你不用每个方法都看一遍再进去找,尽量一条线索跟到底,就读一个方法,由浅入深再看一遍;
4、无关细节略过
有些边缘性的东西,在你读第一遍的时候,没有必要的时候,可以略过;
接着上一篇的ReentrantLock,它的内部实现的核心是 CAS操作+volatile;
下面我们就来一步一步解读源码:
一、准备工作(需要了解 ReentrantLock 用法,ReentrantLock的构造方法, 类之间的关系等等)
1、一般的使用方法以及构造方法:
一般使用方法:
final Lock lock = new ReentrantLock();
构造方法:
public ReentrantLock() { sync = new NonfairSync(); }
2、下面看一下ReentrantLock类:
ReentrantLock这个类有三个内部类:Sync、NonfairSync、FairSync,他们之间存在了一系列的关系
因为 ReentrantLock 默认为非公平锁,类图就以 NonfairSync为例(公平锁的类图类似):
二、源码分析<一>:lock -> acquire -> tryAcquire -> nonfairTryAcquire->返回Boolean类型值
首先你要学会画泳道图,下面先简单看一下泳道图:
下面看一下代码的执行顺序:
2.1、调用Sync类的lock方法:
/** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock();
2.2、Sync类的lock方法由具体的字类(NonfairSync)来实现:
首先进行 compareAndSetState 操作将state修改为1,并把当前线程设置为这把锁的独占线程;
具体的代码如下:
/**内部类NonfairSync(非公平锁)继承了Sync*/ static final class NonfairSync extends java.util.concurrent.locks.ReentrantLock.Sync { /** * 尝试着直接去修改state 修改成功证明当前没有线程持有这把锁 然后设置当前独占访问的线程 * 如果修改失败 */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } }
2.3、AQS.compareAndSetState AQS(AbstractQueuedSynchronizer):
//执行CAS操作 使用了unsafe类的原子操作 多线程的情况下只能有一个线程修改成功 protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
2.4、AQS.acquire (当compareAndSetState修改当前线程独占失败)
/** * 这里是先执行tryAcquire(arg)方法,也就是尝试获取锁的功能: * 1.如果返回tryAcquire(arg)为true说明获取锁成功,也就是!tryAcquire(arg)为false, * 则不会执行后面的acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法了,直接执行业务代码去了 * * 2.如果返回tryAcquire(arg)为false说明获取锁失败,也就是!tryAcquire(arg)为true, * 则会再去执行后面的acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法 * * 所以我们下面要分析下tryAcquire(arg)方法做了什么 * @param arg */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
2.5、NonfairSync.tryAcquire
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
2.6、Sync.nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); //获取当前同步锁的状态,判断state如果等于0 说明当前没有线程独占这把锁 int c = getState(); if (c == 0) { //CAS操作修改state = 1 并设置为当前线程独占 返回true if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //因为这里有锁重入的概念 所以判断当前持有这把锁的线程和current是不是同一个线程 else if (current == getExclusiveOwnerThread()) { //如果是同一个线程 state+1 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
三、源码分析<二>:tryAcquire-> addWaiter-> acquireQueued
解读AQS源码之前你需要了解AQS的核心是CAS+volatile,这里所谓的CAS是因为在多线程的情况下,没有使用synchronized来同步而是使用了大量的CAS操作(例如线程节点获取锁和加到尾节点上都使用了CAS操作),内部还维护了一个双向链表有头节点和尾节点,定义了一个 private volatile int state 具体的含义由子类自己决定,ReentrantLock中state的含义是:当state等于0时证明当前锁没有任何的线程占有,state=1时,证明有线程占有这把锁,记住这里因为ReentrantLock具有可重入性,所以重入一次state加一,当state减少至0时,释放锁。
利用CAS操作替换synchronized锁整条链表:
内部维护了一条双向链表,有头节点和尾节点,新进来的线程节点利用CAS操作获取当前锁,或者加到尾节点
首先也简单画一个流程图:
3.1、AQS.addWaiter方法 (如果tryAcquire的返回值为false证明直接设置当前线程为独占锁失败了)
static final class Node { //定义了一个node节点 static final Node EXCLUSIVE = null; /** waitStatus value to indicate thread has cancelled waitStatus值表示线程已被取消*/ static final int CANCELLED = 1; /** waitStatus value to indicate successor‘s thread needs unparking waitStatus值,表示后续线程需要解锁*/ static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition waitStatus值表示线程正在等待状态*/ static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate 状态需要向后传播 */ static final int PROPAGATE = -3; //构造方法 Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } //头节点 private transient volatile Node head; //尾节点 private transient volatile Node tail; /** * The synchronization state. 锁的状态 */ private volatile int state; volatile int waitStatus; //前一个node节点 volatile Node prev; //后一个node节点 volatile Node next; //线程 volatile Thread thread; //后面等待的节点 Node nextWaiter; }
private Node addWaiter(Node mode) { //调用构造方法 Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; //队列的尾部不为空证明 if (pred != null) { node.prev = pred; //使用CAS操作 将等待队列的双向链表 当前节点设置为尾部 if (compareAndSetTail(pred, node)) { //之前的尾部的下一个节点指向新加入的节点 pred.next = node; //返回节点 return node; } } //如果队列没有尾节点 enq(node); return node; }
3.2、AQS.enq方法 如果尾节点为空,执行完整的添加节点操作
/** * 执行完整的添加节点操作 * @param node * @return */ private Node enq(final Node node) { for (;;) { Node t = tail; //判断为节点是否为空 if (t == null) { // Must initialize //CAS操作把当前线程节点设置为头节点 if (compareAndSetHead(new Node())) //此时头节点和尾节点都为当前节点 tail = head; } else { //如果此时已经有别的节点加入 CAS操作 把当前节点设置为尾节点 node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
3.3、AQS.acquireQueued
//以排他不可中断模式获取已经进入的线程队列 //由条件等待方法和获取方法使用 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); //前驱节点是否为头节点 if (p == head && tryAcquire(arg)) { //当前节点设置为头节点 setHead(node); p.next = null; // help GC failed = false; return interrupted; } //获取同步状态失败 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //线程进入等待状态 interrupted = true; } } finally { if (failed) //取消正在进行的获取尝试 cancelAcquire(node); } }
总结:AQS源码主要核心在于CAS操作和volatile 利用CAS操作代替了Synchronized ,volatile 修饰的state(锁的状态)下维护着一个双向链表;
原文:https://www.cnblogs.com/dongl961230/p/13373137.html