CountDownLatch和CyclicBarrier

CountDownLatch类可以实现两种角色的线程等待对方的效果;
CyclicBarrier类可以使同类现成互相等待达到同步的效果;
这两个类可以更加完善的实现现成对象之间的同步性,对线程对象执行的轨迹控制更加方便。

CountDownLatch的使用

它所提供的功能是判断count计数不为0时则当前线程呈wait状态,也就是屏障处等待。
它也是一个同步功能的辅助类,使用效果是给定一个计数,当使用这个CountDownLatch类的线程计数不为0时,则呈wait状态,如果为0时则继续运行。
实现等待与继续运行的效果分别需要使用await()和countDown()方法来进行。调用await()方法时判断计数是否为0,如果不为0则呈等待状态,其他线程可以调用countDown()方法将计数减1,当计数减到0时,呈等待状态的线程继续运行。
这里还有一个问题是它的计数是无法被重置的,仅仅是减法操作。

简单示例

public class App {
    public static void main(String[] args) throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        new TestThread(latch).start();
        Thread.sleep(2000);
        System.out.println("XX");
        latch.countDown();
    }
}
class TestThread extends Thread {
    private CountDownLatch latch;
    public TestThread(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        try {
            System.out.println("A");
            latch.await();
            System.out.println("B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

A
XX
B

方法介绍

await(long timeout, TimeUnit unit)可设置最大等待时间
getCount()获取当前的计数个数

CyclicBarrier的使用

它不进有CountDownLatch所具有的功能,还可以实现屏障等待的功能,也就是阶段性同步,它在使用上的意义在于可以循环的实现线程要一起做任务的目标,而不是像CountDownLatch一样,仅仅支持一次线程与同步点阻塞的特性。
它也是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点,这些线程必须实时的互相等待,同时它的公共屏障点可以重用,所以类名中包含Cyclic一词。

简单示例

public class App {
    public static void main(String[] args) throws Exception {
        CyclicBarrier cb = new CyclicBarrier(5, new Runnable() {
            @Override public void run() {
                System.out.println("都到了");
            }
        });
        for (int i = 0; i < 5; i ++) {
            new TestThread(cb).start();
        }
    }
}
class TestThread extends Thread {
    private CyclicBarrier cb;
    public TestThread(CyclicBarrier cb) {
        this.cb = cb;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(((int) Math.random()) * 1000);
            System.out.println(Thread.currentThread().getName() + "到了,time=" + System.currentTimeMillis());
            cb.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

运行结果

Thread-0到了,time=1525274424626
Thread-4到了,time=1525274424627
Thread-2到了,time=1525274424626
Thread-1到了,time=1525274424627
Thread-3到了,time=1525274424627
都到了

这里演示的时线程个数等于屏障个数的情况,
下面示例线程数大于屏障个数时,分批处理:

public class App {
    public static void main(String[] args) throws Exception {
        CyclicBarrier cb = new CyclicBarrier(2, new Runnable() {
            @Override public void run() {
                System.out.println("都到了");
            }
        });
        for (int i = 0; i < 4; i ++) {
            new TestThread(cb).start();
            Thread.sleep(500);
        }
    }
}
class TestThread extends Thread {
    private CyclicBarrier cb;
    public TestThread(CyclicBarrier cb) {
        this.cb = cb;
    }
    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + "到了,等待凑齐两个,time=" + System.currentTimeMillis());
            cb.await();
            System.out.println(Thread.currentThread().getName() + "已经凑齐两个,time=" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

运行结果

Thread-0到了,等待凑齐两个,time=1525274766393
Thread-1到了,等待凑齐两个,time=1525274766895
都到了
Thread-1已经凑齐两个,time=1525274766896
Thread-0已经凑齐两个,time=1525274766896
Thread-2到了,等待凑齐两个,time=1525274767398
Thread-3到了,等待凑齐两个,time=1525274767901
都到了
Thread-3已经凑齐两个,time=1525274767901
Thread-2已经凑齐两个,time=1525274767902

方法介绍

getParties()取得屏障点个数
getNumberWaiting()获取当前有几个线程已经达到屏障点
isBroken()查询此屏障是否处于损坏状态
这里需要注意的是CyclicBarrier对于线程的中断interrupte处理会使用全有或者全无的破坏模型
意思是如果一个线程处于中断或超时提前离开屏障点,其他所有屏障点等待的线程均会跑出BrokenBarrierException或者InterruptedException,并且离开屏障点。
await(long timeout, TimeUnit unit)指定在屏障点的最大等待时间,超时将抛出TimeoutException
reset()重置屏障
处于屏障点等待的线程将抛出BrokenBarrierException