功能: 都是用来实现线程同步
CountDownLatch
CountDownLatch 基于 AQS (AbstractQueuedSynchronizer)
CountDownLatch 中有一个内部类 Sync ,Sync 继承自AbstractQueuedSynchronizer .
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CountDownLatchExample { private ExecutorService executorService; private CountDownLatch countDownLatch; private int parties; public static void main(String[] args){ CountDownLatchExample countDownLatchExample = new CountDownLatchExample(10); try { countDownLatchExample.example(); } catch (InterruptedException e) { e.printStackTrace(); } } public CountDownLatchExample(int parties) { executorService = Executors.newFixedThreadPool(parties); countDownLatch = new CountDownLatch(parties); this.parties = parties; } public void example() throws InterruptedException { for (int i = 0; i < parties; i++) { executorService.submit(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " gets job done"); countDownLatch.countDown(); // 线程完成任务之后countDown }); } // 等待所有的任务完成 countDownLatch.await(); System.out.println(Thread.currentThread().getName() + " reach barrier"); executorService.shutdown(); } }
CountDownLatch 主要的方法: countDown 和 await .CountDownLatch 实例化时将设置 AQS 的 state,每次 countDown 时,CAS 将 state 设置为 state -1,await 时首先
检测 当前 state 是否为 0,如果为0 则表示所有的任务完成了,await 结束。否则主线程将循环重试,直到线程被中断或者任务完成或者等待超时。
CountDownLatch 源码分析
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); // 设置Sync(AQS)的state为count } private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; Sync(int count) { setState(count); // 设置AQS的state为count } int getCount() { return getState(); } // protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; // 重写了AQS的方法 } protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { // 循环CAS重试 int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } } }
CountDownLatch 构造参数 count 代表当前参与同步的线程数目,然后设置当前AQS 的状态为 count. Sync 重写 tryAcquireShare 和 tryReleaseShared 方法。
tryAcquireShared 方法会判断当前 AQS 的state 是否为0,如果是0才能获取成功返回1,否则获取失败-1.tryReleaseShared 通过 for 循环进行CAS 设置状态。
CountDownLatch 中最重要的两个方法是 : countDown 和 await
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } public void countDown() { sync.releaseShared(1); }
每次一个任务完成后,调用CountDownLatch 的 countDown 方法,将当前AQS 的state -1.await 方法体调用 AQS的acquireSharedInterruptlibly 或者 tryAcquireShareNanos 方法。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) // tryAcquireShared这里已经在Sync中重写了 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(); // 对于CountDownLatch来说,等待队列中其实只有一个main线程在等待,因此这里第一次就应该判断条件`p == head`成立 if (p == head) { int r = tryAcquireShared(arg); // 方法已经在CountDownLatch$Sync中重写了 if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } // 如果当前失败,则挂起线程,循环重试 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
CyclicBarrier
CyclicBarrier 相比 CountDownLatch 而言多了两个功能
1.支持重置状态,达到循环利用的目的。CyclicBarrier 中有一个内部类 Generation ,代表当前的同步状态处于哪一个阶段。当最后一个任务完成,执行任务的线程会通过 nextGeneration 方法来重置Generation .也可以通过 CyclicBarrier
的 reset 方法重置Generation.
2.支持 barrierCommand ,当最后一个任务运行完成,执行任务的线程会检查 CyclicBarrier 的 barrierCommand 是否为 null,如果不为 null,则运行该任务。
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CyclicBarrierExample { private ExecutorService executorService; private CyclicBarrier cyclicBarrier; private int parties; public CyclicBarrierExample(int parties) { executorService = Executors.newFixedThreadPool(parties); cyclicBarrier = new CyclicBarrier(parties, () -> System.out.println(Thread.currentThread().getName() + " gets barrierCommand done")); this.parties = parties; } public static void main(String[] args) { CyclicBarrierExample cyclicBarrierExample = new CyclicBarrierExample(10); cyclicBarrierExample.example(); } public void example() { for (int i = 0; i < parties; i++) { executorService.submit(() -> { try { Thread.sleep(1000); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " gets job done"); }); } executorService.shutdown(); } }
CyclicBarrier 主要的 API 就是 await 方法,每个任务最后调用这个方法,每个任务最后调用这个方法等待最后一个任务完成,在这之前所有的线程都会等待。
CyclicBarrier 源码分析
参考:https://www.cnblogs.com/darendu/p/10875205.html
/** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); // 锁 /** Condition to wait on until tripped */ private final Condition trip = lock.newCondition(); // 锁关联的Condition,用于线程同步 /** The number of parties */ private final int parties; // 多少个任务参与同步 /* The command to run when tripped */ private final Runnable barrierCommand; // 最后一个任务运行线程应该执行的command /** The current generation */ private Generation generation = new Generation(); // 当前CyclicBarrier所处的Generation /** * Number of parties still waiting. Counts down from parties to 0 * on each generation. It is reset to parties on each new * generation or when broken. */ private int count; // 剩余的等待的任务数目,取值范围:[0-parties]
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); // 等待所有任务完成 } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } } public void reset() { final ReentrantLock lock = this.lock; lock.lock(); try { breakBarrier(); // break the current generation // 开启下一个Generation,达到循环使用的目的 nextGeneration(); // start a new generation } finally { lock.unlock(); } }
当每个任务结束调用 CyclicBarrier 的await 方法,所有的线程都会等待最后一个任务完成才会退出。
总结:
1.CountDownLatch 和 CyclicBarrier 都是用作线程同步, CountDownLatch 基于 AQS ,CyclicBarrier 基于 ReentrentLock
2.CyclicBarrier 支持复用 barrierCommand ,但是 CountDownLatch 不支持
3.CyclicBarrier 会阻塞线程,在最后与i个任务线程执行完之前,其余的线程都必须等待,而线程在调用CountDownLatch 的 countDown 方法之后就会结束。