JUC并发工具阅读

23 阅读14分钟

AQS

用于快速实现同步器的抽象父类,这里面定义了基本的一些实现

// 需要重点关注的属性
// 同步器状态
private volatile int state;
// 等待队列的头节点  lazily initialized
private transient volatile Node head;
// 等待队列的尾节点  lazily initialized
private transient volatile Node tail;
独占锁的抽象逻辑结构

AQS 中独占锁的获取锁的抽象逻辑结构: acquirerelease 方法的具体实现即可:

// acquire方法的具体实现过程(独占模式获取锁,不响应中断)
public final void acquire(int arg) {  
	// 直接尝试获取锁资源,未成功尝试加入等待队列
    if (!tryAcquire(arg) &&  
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  
        selfInterrupt();  
}
// 加入等待队列就是懒惰创建对应的等待节点
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;  
    // 尾节点为空代表等待队列为空,需要插入一个dummy哨兵节点,直接进入enq
    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 t = tail;  
        // 尝试初始化头节点
        if (t == null) { // Must initialize  
            if (compareAndSetHead(new Node()))  
                tail = head;  
        } else {  
	        // 加到尾部去
            node.prev = t;  
            if (compareAndSetTail(t, node)) {  
                t.next = node;  
                return t;  
            }        
        }    
    }
}
// 尝试进行排队
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)) {  
	            // 成功之后将该节点作为头节点,原有的哨兵头节点的依赖解除,快速实现GC
                setHead(node);  
                p.next = null; // help GC  
                failed = false;  
                return interrupted;  
            } 
            // 判断是否应该阻塞,若应该就尝试阻塞
            if (shouldParkAfterFailedAcquire(p, node) &&  
                parkAndCheckInterrupt())  
                interrupted = true;  
        }    
    } finally {  
        if (failed)  
            cancelAcquire(node);  
    }
}
// 根据前驱节点的状态判断是否应该阻塞该节点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {  
    int ws = pred.waitStatus;  
    // 前驱节点的状态为-1:SIGNAL 尝试唤醒后继节点则需要阻塞,返回true
    if (ws == Node.SIGNAL)  
        /*  
         * This node has already set status asking a release         
         * to signal it, so it can safely park.         
        */        
        return true;  
    // 前驱节点状态为正数,表示被取消,需要跳过前驱节点找到更前面状态 <= 0的节点
    if (ws > 0) {  
        /*  
         * Predecessor was cancelled. Skip over predecessors and         
         * indicate retry.        
         */        
        do {  
            node.prev = pred = pred.prev;  
        } while (pred.waitStatus > 0);  
        // 更改该节点的前驱节点,并返回不阻塞,尝试重试
        pred.next = node;  
    } else {  
        /*  
         * waitStatus must be 0 or PROPAGATE.  Indicate that we         
         * need a signal, but don't park yet.  Caller will need to         
         * retry to make sure it cannot acquire before parking.         
	     */ 
	     // 前驱节点为0,需要改为-1,并在接下来的情况重试
	     // 前驱节点为-3,涉及到共享锁
	     compareAndSetWaitStatus(pred, ws, Node.SIGNAL);  
    }    
    return false;  
}

public final boolean release(int arg) {  
	// 释放逻辑具体实现
    if (tryRelease(arg)) {  
        Node h = head;  
        if (h != null && h.waitStatus != 0)  
	        // 尝试唤醒阻塞队列的第一个节点
            unparkSuccessor(h);  
        return true;  
    }    
    return false;  
}

// 独占模式,但是响应中断:
public final void acquireInterruptibly(int arg)  
        throws InterruptedException {  
    if (Thread.interrupted())  
        throw new InterruptedException();  
    if (!tryAcquire(arg))  
        doAcquireInterruptibly(arg);  
}

private void doAcquireInterruptibly(int arg)  
    throws InterruptedException {  
    final Node node = addWaiter(Node.EXCLUSIVE);  
    boolean failed = true;  
    try {  
        for (;;) {  
            final Node p = node.predecessor();  
            if (p == head && tryAcquire(arg)) {  
                setHead(node);  
                p.next = null; // help GC  
                failed = false;  
                return;  
            }            
        if (shouldParkAfterFailedAcquire(p, node) &&  
                parkAndCheckInterrupt())  
                // 直接向上抛出异常,响应中断
                throw new InterruptedException();  
        }    
    } finally {  
        if (failed)  
            cancelAcquire(node);  
    }
}
共享锁的抽象逻辑结构

共享模式下的获取锁的抽象逻辑结构:acquireSharedreleaseShared(同样需要区分是否响应中断

public final void acquireShared(int arg) { 
	// 尝试获取共享锁,这里不再只有成功或者失败,而是根据存在的值来判断成功与否
    if (tryAcquireShared(arg) < 0)  
        doAcquireShared(arg);  
}
// 尝试加入等待队列
private void doAcquireShared(int arg) {  
    final Node node = addWaiter(Node.SHARED);  
    boolean failed = true;  
    try {  
        boolean interrupted = false;  
        for (;;) {  
            final Node p = node.predecessor();  
            // 是头节点
            if (p == head) {  
	            // 获取共享资源
	            
                int r = tryAcquireShared(arg);  
                if (r >= 0) {  
                    setHeadAndPropagate(node, r);  
                    p.next = null; // help GC  
                    if (interrupted)  
                        selfInterrupt();  
                    failed = false;  
                    return;  
                }            
            }            
            if (shouldParkAfterFailedAcquire(p, node) &&  
                parkAndCheckInterrupt())  
                interrupted = true;  
        }    
    } finally {  
        if (failed)  
            cancelAcquire(node);  
    }
}

private void setHeadAndPropagate(Node node, int propagate) {  
    Node h = head; // Record old head for check below  
    setHead(node);  
    /*  
     * Try to signal next queued node if:     
     *   Propagation was indicated by caller,     
     *     or was recorded (as h.waitStatus either before    
     *     or after setHead) by a previous operation     
     *     (note: this uses sign-check of waitStatus because     
     *      PROPAGATE status may transition to SIGNAL.)     
     * and     
     *   The next node is waiting in shared mode,    
     *     or we don't know, because it appears null     
     *     
     * The conservatism in both of these checks may cause     
     * unnecessary wake-ups, but only when there are multiple     
     * racing acquires/releases, so most need signals now or soon     
     * anyway.     
	*/    
	if (propagate > 0 || h == null || h.waitStatus < 0 ||  
        (h = head) == null || h.waitStatus < 0) {  
        Node s = node.next;  
        if (s == null || s.isShared())  
            doReleaseShared();  
    }
} 
内部类 ConditionObjectNode 的解析

Node类解析 Node 类是 AQS 中等待的队列的一个单元,也是一个节点,每个线程都是以 Node 的形式存在的。主要属性如下:

// 等待状态:
// CANCELLED =  1;   表示被取消
// SIGNAL    = -1;   有责任唤醒后继节点
// CONDITION = -2;   存在于等待队列中
// PROPAGATE = -3;   用于共享锁
// 默认为0       ;    表示未设置状态
volatile int waitStatus;
// 前指针
volatile Node prev;
// 后指针
volatile Node next;
// 当前节点对应的线程
volatile Thread thread;

ConditionObject 类解析 用于条件变量的类对象,实现按条件唤醒对应的线程。

// 关键代码解析
// 第一个等待对象head
private transient Node firstWaiter;
// 最后一个等待对象tail
private transient Node lastWaiter;
// 所有条件变量等待的入口函数
public final void await() throws InterruptedException {  
    if (Thread.interrupted())  
        throw new InterruptedException();  
    // 创建一个对应的等待队列节点
    Node node = addConditionWaiter();  
    // 释放掉该线程的所持有的锁资源,
    int savedState = fullyRelease(node);  
    int interruptMode = 0;  
    // 判断是否存在于同步队列中(因为后续的signal会将条件节点放到同步队列中)
    while (!isOnSyncQueue(node)) {  
	    // 在条件队列中就直接阻塞
        LockSupport.park(this);
        // 判断是否中断  
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)  
            break;  
    }    
    // 尝试获取锁,不行就在同步队列阻塞
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)  
        interruptMode = REINTERRUPT;  
    if (node.nextWaiter != null) // clean up if cancelled  
	    // 清除条件队列,删除不是Condition的节点
        unlinkCancelledWaiters();  
    if (interruptMode != 0)  
	    // interrupt处理或者throw InterruptedException
        reportInterruptAfterWait(interruptMode);  
}
// 唤醒函数
public final void signal() {  
    if (!isHeldExclusively())  
        throw new IllegalMonitorStateException();  
    Node first = firstWaiter;  
    if (first != null)  
	    // 具体的唤醒逻辑
        doSignal(first);  
}

private void doSignal(Node first) {  
    do {  
        if ( (firstWaiter = first.nextWaiter) == null)  
            lastWaiter = null;  
        first.nextWaiter = null;  
        // 将该条件队列的第一个节点转移到同步队列中,不成功就继续找后续节点
    } while (!transferForSignal(first) &&  
             (first = firstWaiter) != null);  
}

final boolean transferForSignal(Node node) {   
	// 将节点状态改为0,准备放入同步队列 
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))  
        return false;  
	// 放入同步队列,返回其前驱节点
    Node p = enq(node);  
    int ws = p.waitStatus;  
    // 修改前驱节点为 -1 用于唤醒该节点
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))  
        LockSupport.unpark(node.thread);  
    return true;  
}

ReentrantLock

一款常用的互斥锁,可以实现公平锁和非公平锁,基于 AQS 进行的实现。都可以实现可重入,体现于 state 的值并非单一的 0 或 1,而是 正数 和 0,正数表示线程的加锁次数,体现可重入。

非公平锁的同步器实现:
// 1. 重写对应的tryAcquire方法,这里存在于ReentrantLock的Sync内部类
final boolean nonfairTryAcquire(int acquires) {  
    final Thread current = Thread.currentThread();  
    int c = getState();  
    if (c == 0) {  
	    // 这里是不同之处。非公平锁,只要发现未加锁就会去尝试获取锁
        if (compareAndSetState(0, acquires)) {  
            setExclusiveOwnerThread(current);  
            return true;  
        }    
    }    
    else if (current == getExclusiveOwnerThread()) {  
        int nextc = c + acquires;  
        if (nextc < 0) // overflow  
            throw new Error("Maximum lock count exceeded");  
        setState(nextc);  
        return true;  
    }    
    return false;  
}
// 2. Lock接口的lock方法:
final void lock() {  
	// 直接尝试获取锁
    if (compareAndSetState(0, 1))  
        setExclusiveOwnerThread(Thread.currentThread());  
    else  
	    // 这里内部会去直接调用nonfairTryAcquire
        acquire(1);  
}
// 3. 释放锁:非公平锁和公平锁同理,两者只在争抢锁存在差别
protected final boolean tryRelease(int releases) {  
    int c = getState() - releases;  
    // 判断释放锁的线程
    if (Thread.currentThread() != getExclusiveOwnerThread())  
        throw new IllegalMonitorStateException();  
    boolean free = false;  
    // 判断可重入次数是否释放完
    if (c == 0) {  
        free = true;  
        // 注意这里先设置 owner 为null,因为后面的 state 是 volatile 修饰的,可以保证该变量可见于所有线程,并且保证 state 被改后共享资源线程安全
        setExclusiveOwnerThread(null);  
    }    
    setState(c);  
    return free;  
}
公平锁的同步器实现:
// 1. 直接尝试获取锁,走下面的具体逻辑,没有了直接的
final void lock() {  
    acquire(1);  
}  
  
/**  
 * Fair version of tryAcquire.  Don't grant access unless * recursive call or no waiters or is first. */protected final boolean tryAcquire(int acquires) {  
    final Thread current = Thread.currentThread();  
    int c = getState();  
    if (c == 0) {  
	    // 没线程拥有锁,需要先判断是否存在有线程等待,有等待的先让等待的获取锁(体现了公平性)
        if (!hasQueuedPredecessors() &&  
	        // 只有没有线程等待,并且自己获取到了锁,才会设置 owner
            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;  
}

LongAdder

原子累加器的实现:通过为不同线程分配不同的槽位,实现多个线程无干扰的并发累加,最终求和,降低 CAS 的冲突,从而提高效率

// LongAdder的累加一定始于add方法
public void add(long x) {  
    Cell[] as; long b, v; int m; Cell a;  
    // cells是累加槽位,
    if ((as = cells) != null || !casBase(b = base, b + x)) {  
        boolean uncontended = true;  
        // 没有初始化cells,长度小于1,对应的槽位未初始化,槽位存在竞争,CAS对应累加槽位失败
        // 上述情况都会进入longAccumulate
        if (as == null || (m = as.length - 1) < 0 ||  
            (a = as[getProbe() & m]) == null ||  
            !(uncontended = a.cas(v = a.value, v + x)))  
            longAccumulate(x, null, uncontended);  
    }}
    
final void longAccumulate(long x, LongBinaryOperator fn,  
                          boolean wasUncontended) {  
    int h;  
    // 判断对应线程是否分配一个槽位
    if ((h = getProbe()) == 0) {  
	    // 未分配,需要分配一个槽位
        ThreadLocalRandom.current(); // force initialization  
        h = getProbe();  
        // wasUncontended标识槽位的竞争,这里true标识没有竞争
        wasUncontended = true;  
    }    
    boolean collide = false;                // True if last slot nonempty  
    for (;;) {  
        Cell[] as; Cell a; int n; long v;  
        // 1. cells已经被初始化了
        if ((as = cells) != null && (n = as.length) > 0) {  
	        // 槽位未被初始化,懒惰创建
            if ((a = as[(n - 1) & h]) == null) {  
                if (cellsBusy == 0) {       // Try to attach new Cell  
	                // 加锁创建对应的cell,乐观创建,先创建再使用
                    Cell r = new Cell(x);   // Optimistically create  
                    if (cellsBusy == 0 && casCellsBusy()) {  
                        boolean created = false;  
                        try {               // Recheck under lock  
                            Cell[] rs; int m, j;  
                            if ((rs = cells) != null &&  
                                (m = rs.length) > 0 &&  
                                rs[j = (m - 1) & h] == null) {  
                                rs[j] = r;  
                                created = true;  
                            }                        
                        } finally {  
                            cellsBusy = 0;  
                        }                   
                        if (created)  
                            break;  
                        // 防止槽位被其他线程竞争创建  
                        continue;           // Slot is now non-empty  
                    }  
                }     
                // 存在扩容,防止争抢扩容,直接进入下一次尝试        
                collide = false;  
            }            
            // 存在竞争,直接尝试下一次,快速失败,不去进行CAS操作
            else if (!wasUncontended)       // CAS already known to fail  
                wasUncontended = true;      // Continue after rehash  
            // CAS尝试累加
            else if (a.cas(v = a.value, ((fn == null) ? v + x :  
                                         fn.applyAsLong(v, x))))  
                break;  
            // cell大小超过CPU核数,或者已经被扩容,直接快速失败,进入下面的逻辑
            else if (n >= NCPU || cells != as)  
                collide = false;            // At max size or stale  
            // 快速失败,直接进入下一轮自旋,防止再扩容争抢资源,若是因为核数过大可以一直防止扩容
            else if (!collide)  
                collide = true;  
            // 扩容逻辑,争抢对应的cellsBusy锁
            else if (cellsBusy == 0 && casCellsBusy()) {  
                try {  
                    if (cells == as) {      // Expand table unless stale  
                    // 两倍扩容
                        Cell[] rs = new Cell[n << 1];  
                        for (int i = 0; i < n; ++i)  
                            rs[i] = as[i];  
                        cells = rs;  
                    }                
                } finally {  
                    cellsBusy = 0;  
                } 
                collide = false;  
                continue;                   // Retry with expanded table  
            } 
            // 不扩容的话,或者扩容成功,需要重新分配槽位
            h = advanceProbe(h);  
        }       
        // 2. cells未被初始化,尝试获取锁 
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {  
            boolean init = false;  
            try {                           // Initialize table  
            // 双检锁,保证确定没有初始化,
                if (cells == as) {  
                // 初始的cells大小为2,并且所有的槽位cell都是懒惰式,使用时才会进行创建
                    Cell[] rs = new Cell[2];  
                    rs[h & 1] = new Cell(x);  
                    cells = rs;  
                    init = true;  
                }            
            } finally {  
                cellsBusy = 0;  
            }            
            // 初始化创建成功会直接结束该方法,否则基于自旋,寻找对应的槽位累加
            if (init)  
                break;  
        }        
        // 3. 初始化失败,存在初始化的竞争,直接尝试累加到base
        else if (casBase(v = base, ((fn == null) ? v + x :  
                                    fn.applyAsLong(v, x))))  
            break;                          // Fall back on using base  
    }  
}

ArrayBlockingQueue

主要使用了 ReentrantLock 通过其创立 Condition 实现 takeput 的协同并发安全操作。 itemstakeIndexputIndexcount 等都是临界区资源,加锁以保证并发的安全性

private static final long serialVersionUID = -817911632652898426L;  
  
/** The queued items */  
final Object[] items;  
  
/** items index for next take, poll, peek or remove */  
int takeIndex;  
  
/** items index for next put, offer, or add */  
int putIndex;  
  
/** Number of elements in the queue */  
int count;  
  
/*  
 * Concurrency control uses the classic two-condition algorithm * found in any textbook. */  
/** Main lock guarding all access */  
final ReentrantLock lock;  
  
/** Condition for waiting takes */  
private final Condition notEmpty;  
  
/** Condition for waiting puts */  
private final Condition notFull;

/**  
 * Inserts element at current put position, advances, and signals. 
 * Call only when holding lock. 
 * 入队操作,获取锁后直接在对应生产者指针部分设置值
*/
private void enqueue(E x) {  
    // assert lock.getHoldCount() == 1;  
    // assert items[putIndex] == null;    final Object[] items = this.items;  
    items[putIndex] = x;  
    if (++putIndex == items.length)  
        putIndex = 0;  
    count++;  
    notEmpty.signal();  
}  
  
/**  
 * Extracts element at current take position, advances, and signals. 
 * Call only when holding lock. 
 * 消费操作,需要关注自定义的迭代器
*/
private E dequeue() {  
    // assert lock.getHoldCount() == 1;  
    // assert items[takeIndex] != null;    final Object[] items = this.items;  
    @SuppressWarnings("unchecked")  
    E x = (E) items[takeIndex];  
    items[takeIndex] = null;  
    if (++takeIndex == items.length)  
        takeIndex = 0;  
    count--;  
    if (itrs != null)  
        itrs.elementDequeued();  
    notFull.signal();  
    return x;  
}
/**  
 * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and the specified access policy. 
 * 
 * @param capacity the capacity of this queue  
 * @param fair if {@code true} then queue accesses for threads blocked  
 *        on insertion or removal, are processed in FIFO order; 
 *        if {@code false} the access order is unspecified. 
 * @throws IllegalArgumentException if {@code capacity < 1}  
 */
public ArrayBlockingQueue(int capacity, boolean fair) {  
    if (capacity <= 0)  
        throw new IllegalArgumentException();  
    this.items = new Object[capacity];  
    lock = new ReentrantLock(fair);  
    notEmpty = lock.newCondition();  
    notFull =  lock.newCondition();  
}

/**  
 * Inserts the specified element at the tail of this queue, waiting * for space to become available if the queue is full. 
 * 
 * @throws InterruptedException {@inheritDoc}  
 * @throws NullPointerException {@inheritDoc}  
 */
 public void put(E e) throws InterruptedException {  
    checkNotNull(e);  
    final ReentrantLock lock = this.lock;  
    lock.lockInterruptibly();  
    try {  
        while (count == items.length)  
            notFull.await();  
        enqueue(e);  
    } finally {  
        lock.unlock();  
    }
}

public E take() throws InterruptedException {  
    final ReentrantLock lock = this.lock;  
    lock.lockInterruptibly();  
    try {  
        while (count == 0)  
            notEmpty.await();  
        return dequeue();  
    } finally {  
        lock.unlock();  
    }}

CopyOnWriteArrayList

该并发容器适用于 读多写少 的场景,因为内部的写操作实现为写时复制操作,而读操作不需要进行加锁 处理,可以使得读操作有极高的响应速度。但是同样的,因为在写操作时先复制原有数组到新数组然后改动,最后赋值会原数组,这一段过程虽然加锁,但是存在一种情况:

  1. 先调用写操作,但是写操作还在执行过程中
  2. 调用读操作,但是读到的是旧数据,写操作可能还在进行数组的复制
  3. 返回旧数据,同时写操作完成 上述的情况就会导致短期的数据不一致问题,但是会快速反应给读操作。
@SuppressWarnings("unchecked")  
private E get(Object[] a, int index) {  
    return (E) a[index];  
}
/**  
 * {@inheritDoc} 
 * 
 * @throws IndexOutOfBoundsException {@inheritDoc}  
 */
 public E get(int index) {  
    return get(getArray(), index);  
}  
  
/**  
 * Replaces the element at the specified position in this list with the 
 * specified element. 
 * 
 * @throws IndexOutOfBoundsException {@inheritDoc}  
 */
 public E set(int index, E element) {  
    final ReentrantLock lock = this.lock;  
    lock.lock();  
    try {  
        Object[] elements = getArray();  
        E oldValue = get(elements, index);  
  
        if (oldValue != element) {  
            int len = elements.length;  
            Object[] newElements = Arrays.copyOf(elements, len);  
            newElements[index] = element;  
            setArray(newElements);  
        } else {  
            // Not quite a no-op; ensures volatile write semantics  
            setArray(elements);  
        }        return oldValue;  
    } finally {  
        lock.unlock();  
    }}

CountDownLatch

闭锁用于实现一扇门,阻止线程通过,直到其到达最终状态,不可重复使用。 其具体实现就是通过自定义一个同步器类,继承至 AQS 实现一个共享锁,后续 countDown 就是减去获取到的共享锁,而 await 用于争抢一个共享锁

/**  
 * Causes the current thread to wait until the latch has counted down to 
 * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.  
 * 
 * <p>If the current count is zero then this method returns immediately. 
 * 
 * <p>If the current count is greater than zero then the current 
 * thread becomes disabled for thread scheduling purposes and lies 
 * dormant until one of two things happen: 
 * <ul> 
 * <li>The count reaches zero due to invocations of the 
 * {@link #countDown} method; or  
 * <li>Some other thread {@linkplain Thread#interrupt interrupts}  
 * the current thread. 
 * </ul> 
 * 
 * <p>If the current thread: 
 * <ul> 
 * <li>has its interrupted status set on entry to this method; or 
 * <li>is {@linkplain Thread#interrupt interrupted} while waiting,  
 * </ul> 
 * then {@link InterruptedException} is thrown and the current thread's  
 * interrupted status is cleared. 
 *
 * @throws InterruptedException if the current thread is interrupted  
 *         while waiting 
 */
public void await() throws InterruptedException {  
    sync.acquireSharedInterruptibly(1);  
}

/**  
 * Decrements the count of the latch, releasing all waiting threads if 
 * the count reaches zero.
 * 
 * <p>If the current count is greater than zero then it is decremented. 
 * If the new count is zero then all waiting threads are re-enabled for 
 * thread scheduling purposes. 
 * 
 * <p>If the current count equals zero then nothing happens. 
 */
public void countDown() {  
    sync.releaseShared(1);  
}

CycleBarrier

栅栏是一种类似于闭锁的对象,可以实现阻塞一组线程,直到某个事件的发生,但是不同的是它可以实现一个线程可以等待其他线程,而闭锁是直接同步到达。并且该栅栏可以重复使用。

// 示例,实现一组线程互相等待,直到所有线程执行完毕就执行接下来的任务,若有一个失败,全部都会失败
class Solver {
  final int N;
  final float[][] data;
  final CyclicBarrier barrier;
  class Worker implements Runnable {
    int myRow;
    Worker(int row) { myRow = row; }
    public void run() {
      while (!done()) {
        processRow(myRow);
        try {
          barrier.await();
        } catch (InterruptedException ex) {
          return;
        } catch (BrokenBarrierException ex) {
          return;
        }
      }
    }
  }
  public Solver(float[][] matrix) {
    data = matrix;
    N = matrix.length;
    Runnable barrierAction =
      new Runnable() { public void run() { mergeRows(...); }};
    barrier = new CyclicBarrier(N, barrierAction);
    List<Thread> threads = new ArrayList<Thread>(N);
    for (int i = 0; i < N; i++) {
      Thread thread = new Thread(new Worker(i));
      threads.add(thread);
      thread.start();
    }
    // wait until done
    for (Thread thread : threads)
      thread.join();
  }
}

具体实现:使用一个 ReentrantLockCondition 共同实现对线程的阻塞: 一共有几个变量需要关注:count 参与者数量,barrierCommand 所有线程完成后需要执行的任务 ReentrantLocktrip 用于协调线程之间的运行关系。generation 用于表示每一代,可以实现循环使用 在这里使用加锁操作,保证 countbroken 屏障状态的更新原子性。

flowchart TB
	A["执行await()"]
	B["获取锁lock"]
	C["count--(原子操作)"]
	D["进入等待队列trip"]
	E{"最后一个任务"}
	F["执行屏障任务"]
	G["下一代屏障,唤醒所有等待的线程"]
	A-->B
	B-->C
	C-->E
	E--不是最后一个-->D
	E--最后一个任务-->F 
	F-->G
/**  
 * Main barrier code, covering the various policies. */private int dowait(boolean timed, long nanos)  
    throws InterruptedException, BrokenBarrierException,  
           TimeoutException {  
    final ReentrantLock lock = this.lock;  
    lock.lock();  
    try {  
        final Generation g = generation;  
  
        if (g.broken)  
            throw new BrokenBarrierException();  
  
        if (Thread.interrupted()) {  
            breakBarrier();  
            throw new InterruptedException();  
        }  
        int index = --count;  
        if (index == 0) {  // tripped  
            boolean ranAction = false;  
            try {  
                final Runnable command = barrierCommand;  
                if (command != null)  
                    command.run();  
                ranAction = true;  
                nextGeneration();  
                return 0;  
            } finally {  
                if (!ranAction)  
                    breakBarrier();  
            }        }  
        // loop until tripped, broken, interrupted, or timed out  
        // 自旋防止虚假唤醒的情况
        for (;;) {  
            try {  
                if (!timed)  
                    trip.await();  
                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();  
                }            
            }  
            if (g.broken)  
                throw new BrokenBarrierException();  
  
            if (g != generation)  
                return index;  
  
            if (timed && nanos <= 0L) {  
                breakBarrier();  
                throw new TimeoutException();  
            }        
         }    
    } finally {  
        lock.unlock();  
    }}