架构师集合之AbstractQueuedSynchronizer(AQS)原理及衍生类

上节我们讲了锁,可以基于volatile + CAS 实现同步锁,这一节我们就来看看java中的锁是怎么利用这一点实现的。

AbstractQueuedSynchronizer

说到锁,我们不得不提到AbstractQueuedSynchronizer(AQS)。AQS其实就是基于volatile+cas实现的锁模板。

public class AbstractQueuedSynchronizer{
    //线程节点
    static final class Node {
        ...
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        ...
    }    
    ....
    //head 等待队列头尾节点
    private transient volatile Node head;
    private transient volatile Node tail;
    // The synchronization state. 同步状态
    private volatile int state;  
    ...
    //提供CAS操作,状态具体的修改由子类实现
    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSet(this, expect, update);
    }
	...
	//线程会先尝试获取锁,失败则封装成Node
	public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
	...
	//CAS加入同步队列的尾部,尝试加锁(可能前驱节点刚好释放锁),否则线程进入阻塞等待
	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;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //执行入队操作
        enq(node);
        return node;
    }
	...
	private Node enq(final Node node) {
      //通过for循环来将node节点置为tail节点
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
          //初始化头结点
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
          //执行成功表明node节点成功置为tail节点,若失败,可能是由其他的线程也在进行此操作
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
}

AQS内部维护一个同步队列,元素就是包装了线程的Node
同步队列中首节点是获取到锁的节点,它在释放锁的时会唤醒后继节点,后继节点获取到锁的时候,会把自己设为首节点。

线程会先尝试获取锁,失败则封装成Node,CAS加入同步队列的尾部。在加入同步队列的尾部时,会判断前驱节点是否是head结点,并尝试加锁(可能前驱节点刚好释放锁),否则线程进入阻塞等待。

这里注意,tryAcquire(arg)尝试获取锁里面没有东西,直接抛异常,why?这个就和抽象方法一样,需要你重写,所以AQS的子类都会重写这个方法。

ConditionObject

//AbstractQueuedSynchronizer.java
public class ConditionObject implements Condition, java.io.Serializable {
    //条件队列;Node 复用了AQS中定义的Node
    private transient Node firstWaiter;
    private transient Node lastWaiter;
    ...
    public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();//构造Node,加入条件队列
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);//挂起线程
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            //notify唤醒线程后,加入同步队列继续竞争锁
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
  }
  ...
  //类似Object.notify
    private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
   }

每个Condition对象内部包含一个Node元素的FIFO条件队列
当一个线程调用Condition.await()方法,那么该线程将会释放锁、构造Node加入条件队列并进入等待状态

调用Condition.signal时,获取条件队列的首节点,将其移动到同步队列并且利用LockSupport唤醒节点中的线程。随后继续执行wait挂起前的状态,调用acquireQueued(node, savedState)竞争同步状态。

最后来个完整的图解:
在这里插入图片描述

ReentrantLock

ReentrantLock实现了Lock接口,并使用内部类Sync(Sync继承AbstractQueuedSynchronizer)来实现同步操作

public class ReentrantLock implements Lock, java.io.Serializable {

 public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

}

看到一个类首先就需要知道它的构造方法有哪些,ReentrantLock有两个构造方法,一个是无参的 ReentrantLock() ;另一个含有布尔参数public ReentrantLock(boolean fair)。后面一个构造函数说明ReentrantLock可以新建公平锁;而Synchronized只能建立非公平锁。

ReentrantLock内部类:

abstract static class Sync extends AbstractQueuedSynchronizer{
    .... 
    final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //直接CAS状态加锁,非公平操作
                if (compareAndSetState(0, acquires)) { 
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
    ...
    //重写了tryRelease
    protected final boolean tryRelease(int releases) {
        c = state - releases; //改变同步状态
        ...
        //修改volatile 修饰的状态变量
        setState(c); 
        return free;
    }
}

static final class NonfairSync extends Sync {
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    ....
    static final class FairSync extends Sync {
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&   
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
    ....    

Sync的子类NonfairSync和FairSync都重写了tryAcquire方法
其中NonfairSync的tryAcquire调用父类的nonfairTryAcquire方法,
FairSync则自己重写tryAcquire的逻辑。其中调用hasQueuedPredecessors()判断是否有排队Node,存在则返回false(false会导致当前线程排队等待锁)

ReentrantReadWriteLock

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * default (nonfair) ordering properties.
     */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * the given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
    ...

ReentrantReadWriteLock实现了ReadWriteLock接口,内部有两个实例对象,ReentrantReadWriteLock.ReadLock readerLock读锁和ReentrantReadWriteLock.WriteLock writerLock写锁。也可以指定是否是公平锁和非公平锁。

ReentrantReadWriteLock内部类:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 6317671515068378041L;
        static final int SHARED_SHIFT   = 16;
        //00000000000000010000000000000000
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        //00000000000000001111111111111111
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        //00000000000000001111111111111111
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
        ...

我们先分析读写锁中的这4个int 常量,其实这4个常量的作用就是区分一个int整数的高16位和低16位的,ReentrantReadWriteLock锁还是依托于state变量作为获取锁的标准,那么一个state变量如何区分读锁和写锁呢?答案是通过位运算,高16位表示读锁,低16位表示写锁。

ReentrantReadWriteLock中的读、写锁各个方法的实现都依赖内部类Sync。Sync继承了AQS,覆写了AQS的独占模式方法和共享模式方法。

abstract static class Sync extends AbstractQueuedSynchronizer {
  static final int SHARED_SHIFT   = 16;
  static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
  static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
  static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
  //获取当前读锁数量(共享+重入),高16位的数值
  static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
  //获取当前写锁数量(重入),低16位的数值
  static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
  Sync() {
    readHolds = new ThreadLocalHoldCounter();
    setState(getState()); // ensures visibility of readHolds
  }
  ...
}

独占模式

Sync覆写AQS的tryAcquire方法,只允许一个线程获取写锁的同步状态成功

 //独占模式
  protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);//获取当前写锁数量
    if (c != 0) {
        //有其他线程持有读锁,返回false
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        //重入
        setState(c + acquires);
        return true;
    }
    //公平锁和非公平锁逻辑 由writerShouldBlock方法来判断
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    //获取同步状态成功,设置持有锁的线程为当前线程
    setExclusiveOwnerThread(current);
    return true;
 }
  • 判断是否有锁,有锁且写锁不存在,证明是读锁,直接返回获取失败。有写锁是不是当前线程,不是返回失败。

  • 判断是否超出最大重入范围。

  • 重入独占锁,直接设置state值。

  • 判断是否是公平锁,非公平锁CAS设置state值,公平锁判断AQS同步队列中是否存在排在该线程之前的的节点,保证先入队的先执行。

Sync覆写AQS的tryRelease方法,定义释放资源的逻辑。由于是独占模式,只会有一个线程同时执行该方法,不会存在并发问题。

//独占模式释放
	protected final boolean tryRelease(int releases) {
	    if (!isHeldExclusively())
	        throw new IllegalMonitorStateException();
	    int nextc = getState() - releases;
	    //判断写锁数量是否为0,0的时候修改持有写锁的线程为null
	    boolean free = exclusiveCount(nextc) == 0;
	    if (free)
	        setExclusiveOwnerThread(null);
	    setState(nextc);
	    return free;
	}
  • 首先判断如果不是持有写锁的线程,调用该方法直接抛出异常

  • state的低16位存储写锁数,没releae一次,低16位减去1,当低16位数值为0的时候,释放写锁成功,修改持有写锁的线程为null

共享模式

Sync覆写AQS的tryAcquireShared方法,定义获取同步状态的逻辑。

//共享模式
	protected final int tryAcquireShared(int unused) {
	    Thread current = Thread.currentThread();
	    int c = getState();
	    //其他线程持有写锁,直接返回-1,获取同步状态失败
	    if (exclusiveCount(c) != 0 &&
	        getExclusiveOwnerThread() != current)
	        return -1;
	    //获取state的高16位数值(读锁数量)
	    int r = sharedCount(c);
	    if (!readerShouldBlock() &&是否是公平锁
	        r < MAX_COUNT &&
	        compareAndSetState(c, c + SHARED_UNIT)) {
	        //读锁数量为0,第一次加读锁,设置firstReader为当前线程
	        if (r == 0) {
	            firstReader = current;
	            firstReaderHoldCount = 1;
	        } else if (firstReader == current) { //读锁重入,如果当前线程是第一个持有该读锁的线程,计数器+1
	            firstReaderHoldCount++;
	        } else {
	            HoldCounter rh = cachedHoldCounter;
	            if (rh == null || rh.tid != getThreadId(current))
	                cachedHoldCounter = rh = readHolds.get();
	            else if (rh.count == 0)
	                readHolds.set(rh);
	            rh.count++;
	        }
	       //成功获取同步状态,返回一个正整数或者0
	        return 1;
	    }
	    //获取同步状态失败 
	    return fullTryAcquireShared(current);
	}
  • 存在写锁,如果持有写锁的是当前线程则允许添加读锁,如果持有写锁的是其他线程则不允许添加读锁。

  • 调用readerShouldBlock来判断是否公平与非公平。 判断读锁数量不要超过最大值,不能超过16位数值的最大值。

  • 存在并发则CAS设置state的值,如果成功则获取读锁的同步状态成功,返回1。

  • 如果获取读锁的同步状态失败,调用fullTryAcquireShared方法,循环再次尝试一遍。

Sync覆写AQS的tryReleaseShared方法,定义释放共享锁的逻辑。

//共享模式释放
protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)//如果是首次获取读锁,那么第一次获取读锁释放后就为空了
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) { //表示全部释放完毕
                    readHolds.remove();  //释放完毕,那么久把保存的记录次数remove掉
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                 // nextc 是 state 高 16 位减 1 后的值
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc)) //CAS设置状态
                    
                    return nextc == 0; //这个判断如果高 16 位减 1 后的值==0,那么就是读状态和写状态都释放了
            }
}
  • 修改firstReader和firstReaderHoldCount变量值,移除readHolds记录次数。

  • for循环加CAS修改state的高16位值,当state的高16位为0的时候,释放成功返回true。

CountDownLatch

CountDownLatch是一个同步工具类,有个别名叫计数器,也叫闭锁。它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

public class CountDownLatch {
	private static final class Sync extends AbstractQueuedSynchronizer {
	    private static final long serialVersionUID = 4982264981922014374L;
	    
	    // 传入初始次数
	    Sync(int count) {
	        setState(count);
	    }
	    // 获取还剩的次数
	    int getCount() {
	        return getState();
	    }
	    // 尝试获取共享锁
	    protected int tryAcquireShared(int acquires) {
	        // 注意,这里state等于0的时候返回的是1,也就是说count减为0的时候获取总是成功
	        // state不等于0的时候返回的是-1,也就是count不为0的时候总是要排队
	        return (getState() == 0) ? 1 : -1;
	    }
	    // 尝试释放锁
	    protected boolean tryReleaseShared(int releases) {
	        for (;;) {
	            // state的值
	            int c = getState();
	            // 等于0了,则无法再释放了
	            if (c == 0)
	                return false;
	            // 将count的值减1
	            int nextc = c-1;
	            // 原子更新state的值
	            if (compareAndSetState(c, nextc))
	                // 减为0的时候返回true,这时会唤醒后面排队的线程
	                return nextc == 0;
	        }
	    }
	}
	public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // 初始化 count 值
        this.sync = new Sync(count);
    }
    public void countDown() {
    	// 调用 releaseShared 每次使同步状态值减 1
        sync.releaseShared(1);
    }
}

CountDownLatch中只包含了Sync一个内部类,它没有公平/非公平模式,所以它算是一个比较简单的同步器了。
在 CountDownLatch 初始化的时候会有一个初始的同步状态值,这个同步状态值可以理解为放行前的所要执行的线程数,每次调用 countDown 方法时就把同步状态值减 1。

我们来看看await方法:

 public void await() throws InterruptedException {
        // 共享式检查是否中断,如果中断抛出异常
        // 调用 tryAcquireShared 方法尝试获取同步状态,当闭锁内的线程执行完毕后尝试获取成功,直接返回
        sync.acquireSharedInterruptibly(1);
    }
	public final void acquireSharedInterruptibly(int arg)
	        throws InterruptedException {
	    if (Thread.interrupted())
	        throw new InterruptedException();
	    // 尝试获取锁,如果失败则排队
	    if (tryAcquireShared(arg) < 0)
	        doAcquireSharedInterruptibly(arg);
	}
	private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
	    final Node node = addWaiter(Node.SHARED);// 往同步队列中添加节点
	    boolean failed = true;
	    try {
	        for (;;) {// 一个死循环 跳出循环只有下面两个途径
	            final Node p = node.predecessor();// 当前线程的前一个节点
	            if (p == head) {// 如果是首节点
	                int r = tryAcquireShared(arg);// 这个是不是似曾相识 见上面
	                if (r >= 0) {
	                    setHeadAndPropagate(node, r);// 处理后续节点
	                    p.next = null; // help GC 这个可以借鉴
	                    failed = false;
	                    return;// 计数值为0 并且为头节点 跳出循环
	                }
	            }
	            if (shouldParkAfterFailedAcquire(p, node) &&
	                parkAndCheckInterrupt())
	                throw new InterruptedException();// 响应打断 跳出循环
	        }
	    } finally {
	        if (failed)
	            cancelAcquire(node);// 如果是打断退出的 则移除同步队列节点
	    }
	}

await 方法会自旋检查同步状态值是否为 0,当不为 0 时会阻塞线程,当为 0 时会直接返回,该方法是支持相应中断的,当线程中断时会抛出异常。因此该方法可以理解为一扇门,只有当指定数量的线程执行完后,才会执行后续的代码。

CyclicBarrier

栅栏类,也叫障碍,似于闭锁,它能阻塞一组线程直到某个事件的发生。栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。

举例:CountDownLatch是线程间的等待,是当前面N个线程全部执行完,第n+1的线程才可以执行!且CountDownLatch只能使用一次;CyclicBarrier是线程内的等待,是当N个线程都执行到一个栅栏状态时,我N个线程才会继续执行!CyclicBarrier可以循环使用;

public class CyclicBarrier {
	// parties表示线程数 
	public CyclicBarrier(int parties) {
        this(parties, null);   // 调用两个参数的构造方法,即下面的构造方法
    }
	// parties表示线程数 barrierAction表示栅栏任务,即最后一个线程到达栅栏后执行的任务
	public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;    // final修饰的 用于下次为count赋值 实现循环功能
        this.count = parties;     // 等待的数量,栅栏一次循环完成会把parties赋值为count
        this.barrierCommand = barrierAction;    // 栅栏任务
    }
    /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();

和计数器类似,实例化的时候指定线程数,也可以加一个额外的任务。

重点是线程中调用的await()方法:

	// 如果当前线程在等待过程中阻塞,则抛出InterruptedException
	//  BrokenBarrierException发生情况较多:①当前线程等待过程中,另外一个线程阻塞或者超时;②栅栏被重置了;③await方法在调用,栅栏被破坏;④由于一个异常导致栅栏任务执行失败
	public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);  // 
        } catch (TimeoutException toe) {  // 超时异常直接抛出错误
            throw new Error(toe); // cannot happen
        }
    }
    
	private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;   // 利用重入锁保证线程的安全性
        lock.lock();
        try {
            final Generation g = generation;   // 栅栏每broken一次,Generation就需要重新生成一个对象
            // 如果当前栅栏broken,则抛出BrokenBarrierException异常;
            if (g.broken)
                throw new BrokenBarrierException();
            // 如果当前线程被阻塞,则栅栏被broken,抛出InterruptedException异常
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
            // 等待的线程数(count)减1
            int index = --count;
            if (index == 0) {  // tripped 若等待线程数为0,即该线程为最后一个到达栅栏的线程
                boolean ranAction = false;
                try {
                    // 当前线程执行栅栏定义的任务
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;
                    nextGeneration();    // 当前栅栏任务完成栅栏功能耗尽,重新定义一个栅栏
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }
            // 如果不是最后一个到达栅栏的线程,则一直循环,直到栅栏被broken,线程被阻塞或者超时
            for (;;) {
                try {
                    if (!timed)   // timed默认为false 则继续执行
                        trip.await();   // 线程等待直到满足condition的情况(栅栏被broken/线程阻塞)
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }
                // 如果栅栏被破坏 抛出BrokenBarrierException异常
                if (g.broken)
                    throw new BrokenBarrierException();
                // 如果当前栅栏的generation对象不一致,返回等待线程数
                if (g != generation)
                    return index;
                // 此处抛出TimeOutException异常 按理来说不会执行,因为timed值为false
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();   // 释放栅栏的锁,便于其他线程调用栅栏的wati()
        }
    }

大体流程就是初始化指定线程数,每次调用await()方法等待的数量减少一,并在for循环里面阻塞。支持相应中断的,当线程中断时会抛出异常。当等待的数量为0时新建一个栅栏,同时唤醒所有阻塞的线程,直接返回0。
注意一点,栅栏类没有像其他锁类一样,有内部类继承AbstractQueuedSynchronizer,它时直接实例化了ReentrantLock和Condition。

Semaphore

Semaphore用于管理信号量,在并发编程中,可以控制返访问同步代码的线程数量。

public class Semaphore implements java.io.Serializable {
	public Semaphore(int permits) {
	        sync = new NonfairSync(permits);
    }
	public Semaphore(int permits, boolean fair) {
	        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
    ...
}

默认是非公平锁,两个构造方法都必须传int permits值。
这个int值在实例化内部类时,被设置为AQS中的state。

Sync(int permits) {
     setState(permits);
}

acquire()获取信号

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

实际调用的是AQS中acquireSharedInterruptibly()的方法。

 public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
}
  • 调用tryAcquireShared()方法尝试获取信号。

  • 如果没有可用信号,将当前线程加入等待队列并挂起。

tryAcquireShared()方法被Semaphore的内部类NonfairSync和FairSync重写,实现有一些区别。

NonfairSync.tryAcquireShared()

final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
}

非公平锁对于信号的获取是直接使用CAS进行尝试的。

FairSync.tryAcquireShared()

protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
}

公平锁先调用hasQueuedPredecessors()方法,判断队列中是否有等待线程。如果有,直接返回-1,表示没有可用信号。队列中没有等待线程,再使用CAS尝试更新state,获取信号。

acquireSharedInterruptibly()方法中,如果没有可用信号加入队列的方法doAcquireSharedInterruptibly(),这个方法是AQS中的方法,上面没讲,所以这里讲解下:

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);   // 加入等待队列
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();   
                if (p == head) {      // 2
                    int r = tryAcquireShared(arg);//如果前继节点是head,则尝试获取锁
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);获取锁成功,设置新head和共享传播(唤醒后继共享节点)
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&     // 3
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);   
        }
}
  • 封装一个Node节点,加入队列尾部。

  • 在无限循环中,如果当前节点是头节点,就尝试获取信号,获取到了就设置新的head和唤醒后继共享节点。

  • 不是头节点,在经过节点状态判断后,挂起当前线程。

release()释放信号

    public void release() {
        sync.releaseShared(1);
    }

调用的是AQS中的releaseShared()方法。

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {//释放共享锁
            doReleaseShared();//唤醒等待的头部线程
            return true;
        }
        return false;
    }

tryReleaseShared()释放,doReleaseShared唤醒等待的头部线程。

至此,AbstractQueuedSynchronizer(AQS)原理及衍生类讲完了,比较多,慢慢看,细节看一遍了解大概就行了,主体流程明白就差不都了。所有的锁都讲完了,底层无非都是CAS+volatile实现,所有锁类都是基于AQS加工。唯一有区别的是栅栏类是唯一一个没有内部类去继承AQS的,而是实例化了重入锁和Conition。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
AQSAbstractQueuedSynchronizer)是 Java 并发包中一个重要的同步组件,它通过内部维护一个 FIFO 队列来实现同步协作,是很多同步组件的基础,例如 ReentrantLock、CountDownLatch、Semaphore 等。 AQS 内部基于一个 int 型的变量 state 来表示同步状态,当 state 为 0 时表示未定状态,当 state 大于 0 时表示已定状态,小于 0 时表示 state 的绝对值是等待获取同步状态的线程个数。AQS 的操作主要包括独占和共享两种模式,独占模式下只有一个线程能够获取同步状态,共享模式下可以有多个线程同时获取同步状态。 AQS 的核心是一个双向链表,其中每个节点表示一个等待线程,通过 CAS 操作来实现节点的加入和移除。在独占模式下,等待线程会被加入到队列的尾部,当前获取同步状态的线程是队列头部的节点,当当前线程释放同步状态后,会唤醒队列中的下一个节点。在共享模式下,等待线程也会被加入到队列的尾部,但是当前获取同步状态的线程可能是队列中的任意一个节点,当当前线程释放同步状态后,会唤醒队列中的所有节点。 AQS 组件包括以下几种: 1. ReentrantLock:可重入,支持公平和非公平两种模式。 2. ReentrantReadWriteLock:读写,支持读写分离。 3. Semaphore:信号量,用于控制资源的并发访问量。 4. CountDownLatch:倒计时门闩,用于等待其他线程完成一定的操作后再执行。 5. CyclicBarrier:循环屏障,用于等待一组线程达到某个状态后再执行。 6. Condition:条件变量,用于在某个条件下等待或唤醒线程。 以上就是 AQS 的基本原理和组件介绍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小明程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值