多线程工具 CyclicBarrier、CountDownLatch

功能: 都是用来实现线程同步

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 方法之后就会结束。

猜你喜欢

转载自www.cnblogs.com/baizhuang/p/11387934.html