Java并发编程AQS(AbstractQueuedSynchronizer)源码分析独占模式(一)

Java并发编程AQS

抽象队列同步器(简称AQS)用来构建锁和其他同步组件的基础框架,如可重入锁ReentrantLock、读写锁ReentrantReadWriteLock、锁存器CountDownLatch等等…
其内部使用一个int类型成员变量state表示同步状态,FIFO(先进先出)同步队列来控制获取共享资源的线程,ConditionObject构建的等待队列来实现条件锁的同步实现,同时支持独占式与共享式获取同步状态,AQS是基于模板方法设计的,同步器子类必须重写其指定方法,下面直接撸源码。

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer
implements java.io.Serializable

同步器继承另一个抽象类AbstractOwnableSynchronizer,看看其中有些什么东西!

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    private static final long serialVersionUID = 3737899427754241961L;
    
    protected AbstractOwnableSynchronizer() { }
    // 独占模式下的线程
    private transient Thread exclusiveOwnerThread;
    // 设置线程为当前独占模式的拥有者
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }
    // 获取独占模式下的线程
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

可以由线程以独占方式拥有的同步器。此类为创建锁和相关同步器(伴随着所有权的概念)提供了基础。AbstractOwnableSynchronizer 类本身不管理或使用此信息。但是,子类和工具可以使用适当维护的值帮助控制和监视访问以及提供诊断。设置和获取独占锁的拥有者线程并提供给子类使用,就是此类的作用。
我们大概认识一下AbstractQueuedSynchronizer类中一部分属性和方法…

// 重要概念:静态内部类Node(节点),构成AQS中同步队列,后续详细分析
static final class Node{......}
// 头节点,注意关键字volatile 
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
/*
*感觉是不是跟链表结构类似啊
*/

// 重要概念:同步状态
private volatile int state;

// protected方法,获取同步状态。子类实现自定义同步组件的时候需要用到
protected final int getState() {
        return state;
}
// 设置(释放)同步状态
protected final void setState(int newState) {
    state = newState;
}
// 尝试获取同步状态,注意该方法是独占式的,留给子类重写,获取成功返回true
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
// 独占式释放同步状态,释放成功返回true
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
// 共享式尝试获取同步状态,获取成功则返回大于等于0
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
// 共享式释放同步状态,释放成功返回true
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}
// 当前线程是否独占锁
protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}
// 重要概率:通过实现Condition接口,构成AQS中等待队列。后续讲解
public class ConditionObject implements Condition, java.io.Serializable {...}

/**
重要概念:Unsafe,通过这个类在静态块中获取相应属性在对象中的偏移量,然后根据偏移量进行Unsafe.CAS方法调用,为什么说并发包是建立在CAS之上的!
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;

static {
    try {
    	// 获取AQS中state属性的偏移量
        stateOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
        // 获取AQS中head属性的偏移量
        headOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
        // 获取AQS中tail属性的偏移量
        tailOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
        // 获取Node中waitStatus属性的偏移量
        waitStatusOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("waitStatus"));
        // 获取Node中next属性的偏移量
        nextOffset = unsafe.objectFieldOffset
            (Node.class.getDeclaredField("next"));

    } catch (Exception ex) { throw new Error(ex); }
}
/* 进行CAS操作 */
private final boolean compareAndSetHead(Node update) {
    return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
private final boolean compareAndSetTail(Node expect, Node update) {
    return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
private static final boolean compareAndSetWaitStatus(Node node,int expect,int update) {
    return unsafe.compareAndSwapInt(node, waitStatusOffset,expect, update);
}
private static final boolean compareAndSetNext(Node node,Node expect,Node update) {
    return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}

当我们实现自定义同步组件时,将会调用AQS对外提供的获取与释放同步状态方法,分为独占模式与共享模式。
AQS独占模式下的获取与释放同步状态方法

// 独占式获取同步状态,尝试获取同步状态,成功直接返回,失败则将当前线程转成Node加入同步队列,如果加入同步队列也失败了,则会中断当前线程
public final void acquire(int arg) {...}

// 独占式可中断获取同步状态,与acquire()方法逻辑类似,但是会响应线程中断抛出InterruptedException异常。
public final void acquireInterruptibly(int arg) throws InterruptedException { ...}

// 在acquireInterruptibly()方法增加了超时限制 nanosTimeout
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {...}
// 独占式的释放同步状态
public final boolean release(int arg) {...}

AQS共享模式下的获取与释放同步状态方法

/*
*共享模式下获取与释放同步状态方法与独占模式差不多,主要区别在于同一时刻可以有多个线程获取到*同步状态。
*/
public final void acquireShared(int arg) { ... }
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { ... }
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException{ ... }
public final boolean releaseShared(int arg) { ... }

后面跟踪方法内代码来看看它是怎么实现独占与共享模式下同步状态的获取与释放的。

AQS中同步队列Node节点结构

static final class Node {
	 // 共享模式下等待的标记节点
	 static final Node SHARED = new Node();
	 // 独占模式中等待的标记节点
	 static final Node EXCLUSIVE = null;
	 // 当前节点的线程处于中断或获取同步状态超时的状态值,该状态下的线程不会再阻塞
	 static final int CANCELLED =  1;
	 // 当前节点的线程处于释放或取消同步状态的状态值,该节点会通知下一节准备获取同步状态
	 static final int SIGNAL    = -1;
	 // 当前节点的线程处于等待队列上的状态值,如果其他线程调用了Condition的singal()方法,该节点会从等待队列转移到同步队列中,等待获取同步状态
	 static final int CONDITION = -2;
	 // 当前节点的线程处于可运行的状态值。
	 static final int PROPAGATE = -3;
	 // 当前节点的线程状态属性
	 volatile int waitStatus;
	 // 当前节点在同步队列中的上一个节点
	 volatile Node prev;
	 // 当前节点在同步队列中的下一个节点
	 volatile Node next;
	 // 当前节点的线程
	 volatile Thread thread;
	 // 当前节点在等待队列中的下一个节点
	 Node nextWaiter;
}

AQS中head、tail分别指向同步队列中的头部节点与尾部节点。在每个节点内部又维护了当前节点的前置节点和后置节点。当某一个线程获取同步状态成功后,其他线程则将构造成node节点,基于CAS比较后将节点加入到队列的尾部,如下图。
在这里插入图片描述
head节点表示当前获取同步状态成功的节点。当head节点释放同步状态时,将会唤醒下一个节点,同时将自己从队列中删除。下一个节点如果获取同步状态成功将会把自己设置为head节点。如下图
在这里插入图片描述
下面我们来看AQS是怎么获取同步状态与释放同步状态的…

// 独占模式同步状态获取
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

里面涉及的两个方法判断,同时满足就调用selfInterrupt()方法,我们先来看下selfInterrupt方法。

// 方法内部调用当前线程的interrupt()方法,设置一个中断标志
static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

条件中的两个方法…

// 一个protected方法,交给子类重写,成功返回true,失败返回false
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
// 由acquire方法得知当尝试获取同步状态失败后会调用acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,先来看看addWaiter(Node.EXCLUSIVE)方法干了什么...
private Node addWaiter(Node mode) {
	// 将当前线程构造成一个独占node节点
    Node node = new Node(Thread.currentThread(), mode);
    // 尾节点
    Node pred = tail;
    // 如果存在尾节点
    if (pred != null) {
    	// 把当前线程构造的node节点的前节点指针指向尾节点,其实就是有意把当前节点设置为尾节点。
        node.prev = pred;
        
        // 通过CAS操作尝试添加,成功就返回ture,为什么需要使用CAS来进行节点添加?
        // 多线程情况下很有可能在if(pred != null)判断之后有某个线程已经释放了,而且当前队列只存在一个节点,那这一个节点释放后会移除队列,此时数据就出现不一致了
        if (compareAndSetTail(pred, node)) {
        	// 如果成功将 前尾节点的下节点指向当前节点,并返回当前节点,此时当前节点已经是尾节点了。
            pred.next = node;
            return node;
        }
    }

    /*
    *前面做了什么,新节点加入到同步队列,首先判断同步队列中是否存在节点,如果存在就将新节点加入到队列尾部
    */
    
    // 如果尾节点不存在,则调用enq方法处理...下面我们来看看enq方法是如何处理不存在尾节点情况的。
    enq(node);
    return node;
}

private Node enq(final Node node) {
	// 一开始就是一个死循环,在并发中通常叫做自旋
    for (;;) {
    	// 尾节点
        Node t = tail;
        // 如果尾节点不存在
        if (t == null) {
        	// 基于CAS创建一个节点,注意使用的是compareAndSetHead方法,通过head的偏移值创建节点。从起始位置创建,因为此时就是个空队列。同样必须使用CAS操作
            if (compareAndSetHead(new Node()))
            	// 此时head与tail指向的是同一个地址
                tail = head;
        } else {
        	// 走到这里其实就是在这个期间当前自旋创建head节点成功,也有可能是其他线程创建的head节点,那既然头节点已经存在了,就直接将当前节点设置成尾节点
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

addWaiter方法逻辑:
1、线程构建成节点后需要添加到同步队列中,首先判断队列中是否则在尾节点,如果存在直接将其添加到队列尾部(注意添加操作是基于CAS的)
2、如果不存在尾节点,那这个同步队列在这一时刻肯定为空。开启自旋,而在循环内部不断判断尾节点是否存在,为什么?因为这是面向多线程的,可能此时同步队列尾节点被其他线程构建成功了。这里有一个疑问就是为什么不使用头节点判断,而去使用尾节点?问题暂时放这里…
3、如果尾节点依然不存在,那试图构建一个全新的节点加入队列头部,这时头尾都是指向同一个节点。这里引申出来一个问题,为什么AQS的设计者在线程获取锁失败后尝试加入到同步队列过程中会自旋判断队列是否为空(从上面判断head节点等于null就可以看出来),此时队列为空的情况下宁愿去构造一个初始化的节点(new Node())让其他线程来竞争,都不愿把此时在尝试加入到队列的节点设置为head节点?
4、最后将当前节点与头节点进行连接,退出自旋。2到4这几步一直处于自旋状态,直到尾节点构建成功才会停止自旋。

如果线程获取同步状态失败,则会将线程构建成node节点加入到同步队列尾部后,还有一个至关重要的方法acquireQueued();

// 此时当前线程已经构建成node节点并且已经加到同步队列的尾部了
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
	try {
	   boolean interrupted = false;
	   // 又是自旋
	   for (;;) {
	   	   // 拿到当前节点的上一个节点
	       final Node p = node.predecessor();
	       // 如果当前节点的上一个节点是head节点,那就是说上节点已经获取到同步状态了,当前线程再次尝试取获取同步状态。
	       if (p == head && tryAcquire(arg)) {
	       	   // 把当前节点设为头节点
	           setHead(node);
	           // 头节点的下节点指针指向null,看上图,当前节点如果获取同步状态成功那头节点将从队列中删除,将自己设为头节点。
	           p.next = null; 
	           // 成功返回
	           failed = false;
	           return interrupted;
	       }
	       //  如果当前节点的上一个节点不是头节点或者获取同步状态失败,则判断线程是否需要阻塞
	       if (shouldParkAfterFailedAcquire(p, node) &&
	           parkAndCheckInterrupt())
	           interrupted = true;
	   }
	} finally {
	   // 等下介绍
	   if (failed)
	       cancelAcquire(node);
	}
}
// 获取同步状态失败后是否需要阻塞 node:当前节点,pred:当前节点的上一个节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
	// 拿到上一节点的状态
    int ws = pred.waitStatus;
    // 如果上一节点的状态是同步释放信号,返回true,当前节点可以阻塞
    if (ws == Node.SIGNAL)
        return true;
    // 通过之前Node节点类属性得知,如果状态大于0则表示上一节点已经中断或者超时了
    if (ws > 0) {
    	// 那么跳过所有线程已经中断的节点,也就是状态为waitStatus=CANCELLED 的节点。直到找到最近未被中断的目标节点,将目标节点的下节点指针指向当前节点。
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
    	// 如果未被中断,基于CAS更新上一节点状态为SIGNAL
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

// 当shouldParkAfterFailedAcquire()反回true时,会调用parkAndCheckInterrupt()方法来阻塞当前线程(此情景一直在自旋中...)
private final boolean parkAndCheckInterrupt() {
	// 使用LockSupport.park()来阻塞线程,LockSupport是一个工具类,提供了线程的阻塞与唤醒功能
    LockSupport.park(this);
    // 返回线程是否中断
    return Thread.interrupted();
}

// acquireQueued方法里finally块判断failed变量,failed变量为true则表示线程已经中断,将会调用cancelAcquire方法,将线程对应node从同步队列中删除...
// 从同步队列中移除该节点,该节点对应线程已经被中断了
private void cancelAcquire(Node node) {
   // 如果节点已经不存在,直接返回
   if (node == null)
       return;
   // 将该节点对应线程置为null
   node.thread = null;
   // 拿到当前节点的上一个节点
   Node pred = node.prev;
   // 如果上一个节点状态大于0,那么上节点也是中断了,一直往上跳过符合这个条件的节点。直到找到目标节点,将目表节点置为当前节点的上节点
   while (pred.waitStatus > 0)
       node.prev = pred = pred.prev;
   // 拿到目标节点中的下节点
   Node predNext = pred.next;
   // 将当前节点状态置为CANCELLED
   node.waitStatus = Node.CANCELLED;
   // 如果当前节点是尾节点  基于CAS将尾节点重新指向,把目标节点设为尾节点
   if (node == tail && compareAndSetTail(node, pred)) {
       // 基于CAS,将目标节点下节点指针指向null
       compareAndSetNext(pred, predNext, null);
   } else {
       // 如果当前节点不是尾节点或者更新失败
       int ws;
       //  1、如果目标节点不是头节点,并且目标节点的状态为SIGNAL(其实目标节点通过上面操作已经变成了当前中断节点的上一个节点了)
       //  2、紧接上一个注释:或者目标节点状态即将为SIGNAL,并且节点内线程不是null
       if (pred != head &&
           ((ws = pred.waitStatus) == Node.SIGNAL ||
            (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
           pred.thread != null) {
           // 拿到当前中断节点的下节点
           Node next = node.next;
           // 如果下节点不是null 并且状态不是处于中断状态
           if (next != null && next.waitStatus <= 0)
           		// CAS更新,将目标节点的下节点指针指向当前中断节点的下节点
               compareAndSetNext(pred, predNext, next);
       } else {
           // 同时不满足注释1和2,调用unparkSuccessor
           unparkSuccessor(node);
       }
     
       node.next = node; 
   }
}

private void unparkSuccessor(Node node) {
  // 拿到被中断节点的状态		
  int ws = node.waitStatus;
  // 如果状态<0,直接CAS重置,为0就是初始状态
  if (ws < 0)
      compareAndSetWaitStatus(node, ws, 0);
  // 拿到当前中断节点的下节点
  Node s = node.next;
  // 如果下节点为null,或者下节点状态也是处于中断,直接移除下节点
  if (s == null || s.waitStatus > 0) {
      s = null;
      // 通过尾节点向前遍历,获取最近的waitStatus<=0的节点
      for (Node t = tail; t != null && t != node; t = t.prev)
          if (t.waitStatus <= 0)
              s = t;
  }
  // 如果节点不为null,直接唤醒
  if (s != null)
      LockSupport.unpark(s.thread);
}

acquireQueued方法的逻辑:
1、线程获取同步状态失败并构建成节点加入到同步队列尾部后,保持自旋,并判断该节点的上一个节点是否是头节点。如果是头部节点,尝试再次获取同步状态。成功结束循环并返回
2、如果1条件不符合,则判断节点是否需要阻塞,shouldParkAfterFailedAcquire会从当前节点的上一个节点开始往头部查找,找到符合条件的第一个节点(未被中断)将其状态设置成SIGNAL,并与当前节点进行连接。为什么需要将状态设置成SIGNAL?因为在它的前置节点释放同步状态时,前置节点会通知状态为SIGNAL的后续节点告诉它准备获取同步状态。一旦设置成功返回true
3、2条件返回true时,节点就可以调用parkAndCheckInterrupt方法进行阻塞了,并返回线程中断状态。再次期间1到3步一直不断自旋直到第一步满足条件为止。
4、如果当前节点中断或超时中断了,执行cancelAcquire方法,判断被中断节点是否是尾节点,如果是,移除该节点,然后往头部遍历找到第一个未被中断的节点并将其设为新的尾节点。如果不是,又有多个条件(多线程下节点可能随时发生变化),(1)、如果该中断节点的上一节点不属于头节点并且状态为SIGNAL,则直接将中断节点的上节点与下节点进行连接。(2)如果上节点未被中断,并且将上节点状态更新成SIGNAL成功,上节点内线程不为null,也是将中断节点的上节点与下节点进行连接。(3)前面两个都不满足,调用unparkSuccessor方法唤醒当前中断节点未被中断的下一个节点

这就是独占模式下获取同步状态的所有代码…写了一上午暂且休息一下…
在这里插入图片描述
独占模式就是一个同一时刻只支持一个线程进行访问。下面看看独占模式释放锁操作…

// tryRelease方法在下面,又是一个模板方法,需要子类重写,返回true则释放成功
public final boolean release(int arg) {
    if (tryRelease(arg)) {
    	// 拿到头节点
        Node h = head;
        // 头节点不为null,并且头节点不是初始化状态,调用unparkSuccessor方法进行释放
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
// 释放节点方法
private void unparkSuccessor(Node node) {
	// 拿到头节点状态
    int ws = node.waitStatus;
    // 如果头节点状态未被中断并且已经占用,则将头节点状态设置成初始状态,等待下个节点使用
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    // 拿到当前线程的下节点
    Node s = node.next;
    // 如果下节点不存在或者下节点被中断
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 从尾部往前遍历找到未被中断的目标节点
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 使用LockSupport类,唤醒目标节点
    if (s != null)
        LockSupport.unpark(s.thread);
}

分析完独占模式的AQS原理后,紧接着看看使用了独占模式的另一个锁的实现,可重入锁ReentrantLock在独占模式的基础上实现了公平锁与非公平锁的概念。
ReentrantLock是一种可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
ReentrantLock默认是非公平模式,通过构造方法可手动指定可重入锁模式。
下面我们大概看看可重入锁的基本源码…

// 实现 了Lock接口
public class ReentrantLock implements Lock, java.io.Serializable {...}

// Lock接口提供了一些列供实现类自己实现的方法,也是我们最常见的方法,这些方法就不多加介绍了
public interface Lock {
	void lock();
	void lockInterruptibly() throws InterruptedException;
	boolean tryLock();
	boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
	void unlock();
	// 获取等待通知组件
	Condition newCondition();
}
// 同步对象
private final Sync sync;
// 这就是ReentrantLock核心实现...继承AQS。
abstract static class Sync extends AbstractQueuedSynchronizer { ... }
// 继承自Sync的非公平锁
static final class NonfairSync extends Sync { ... }
// 继承自Sync的公平锁
static final class FairSync extends Sync { ... }
// 无参构造,默认是非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 有参构造,可手动指定模式
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
// 后面方法都是实现了Lock接口的方法,内部都是通过操作sync对象实现的。
public void lock() {
    sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
}
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
   return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
   sync.release(1);
}
public Condition newCondition() {
        return sync.newCondition();
}

Sync抽象静态内部类,我们看看他是怎么重写AQS方法的

// 获取同步状态方法,加锁的起点
abstract void lock();
// 非公平模式尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
	// 拿到当前线程
    final Thread current = Thread.currentThread();
    // 调用getState:父类方法,获取同步状态state值
    int c = getState();
    // 如果state值为0,直接进行CAS比较交换
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
        	// 把当前线程设为独占线程,并返回true
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 注意:此处实现锁的可重入,如果独占锁的线程就是当前线程
    else if (current == getExclusiveOwnerThread()) {
    	// 将值相加后,并设置state属性,返回true。这就是为什么重每次加锁后都需要在finally里面进行释放。
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

// 释放锁
protected final boolean tryRelease(int releases) {
	// 释放锁一次就需要减1
    int c = getState() - releases;
    // 如果释放锁的当前线程,不是独占这个锁的线程,则抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果state-1后为0,则代表已经没有线程占用当前锁了
    if (c == 0) {
    	// 设置返回值为true,并清空独占线程
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 更新state值
    setState(c);
    return free;
}

再看看Sync子类NonfairSync与FairSync的实现

// 非公平模式
static final class NonfairSync extends Sync {
 private static final long serialVersionUID = 7316153563782823691L;
 	// 获取同步状态(获取锁)入口
 final void lock() {
 	   // 非公平模式,什么都不考虑,直接上来就是使用CAS方法获取锁。更新成功将当前线程设置成独占线程
     if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
     else
     	   //否则调用父类模板方法,上面阅读AQS抽象同步器的时候讲过,AQS中获取同步失败将会加入同步队列。
         acquire(1);
 }
 // 尝试获取同步方法,直接调用父类方法
 protected final boolean tryAcquire(int acquires) {
     return nonfairTryAcquire(acquires);
 }
}

// 公平模式
static final class FairSync extends Sync {
	private static final long serialVersionUID = -3000897897090466540L;
	// 公平与非公平区别,这里是会调用AQS模板方法,通常同步失败,当前线程将会构建成	Node节点加入同步队列尾部
	final void lock() {
	    acquire(1);
 }

 // 尝试获取锁
protected final boolean tryAcquire(int acquires) {
	// 拿到当前线程
    final Thread current = Thread.currentThread();
    // 拿到state
    int c = getState();
    // 如果当前锁未被线程独占
    if (c == 0) {
    	// 1、获取队列中下一个线程 ,hasQueuedPredecessors见下说明,返回false则向下走。公平与非公平的处理区别主要体现在这里
    	// 2、CAS获取同步状态
    	// 3、设置独占线程。
        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;
	}
}

// 
public final boolean hasQueuedPredecessors() {
 	// 拿到队列头节点、尾节点
	Node t = tail; // Read fields in reverse initialization order
	Node h = head;
	Node s;
	// 1、如果头节点不等于尾节点,并且满足2条件,返回true
	// 2、头节点的下一个节点等于null 或者下节点的线程不是当前线程
	return h != t &&
	    ((s = h.next) == null || s.thread != Thread.currentThread());
}

总结:通过AQS源码然后再进入ReentrantLock可重入锁的源码,发现阅读ReentrantLock的实现比较简单,而且我们阅读并发编程包下面的源码,只要从上往下走,思路会越来越清晰。后续继续写AQS共享锁与ReentrantReadWriteLock的实现部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值