CountDownLatch与CyclicBarrier是在java1.5被引入的线程同步工具类。
CountDownLatch
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(不能用作互斥)。可用于:
- 让多个线程等待:模拟并发,让并发线程一起执行。初始化一个共享的CountDownLatch(1),多个线程在开始执行任务前先countdownlatch.await()等待,当主线程调用countDown()时,计数器变为0,多个线程同时被唤醒。
- 让单个线程等待:某一线程在开始运行前等待n个线程执行完毕。初始化一个 CountDownLatch(n),每当一个任务线程执行完毕,就调用countdownLatch.countDown()将计数器减1 ,当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。
CountDownLatch是一次性的,其值只能在构造方法中初始化,当CountDownLatch使用完毕后,它不能再次被使用。
主要方法说明
public void countDown()
:递减计数,当计数到达零,则释放所有等待的线程。
public boolean await(long timeout,TimeUnit unit)
或await()
:等待计数器:
- 计数到达零,则该方法返回true值;
- 超出了指定的等待时间,则返回值为false;如果该时间小于等于零,则该方法根本不会等待。
等待其他线程完成示例
构造与等待线程数量相等的计数器,并在每个线程执行完成后调用countDown:
CountDownLatch countLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
int index = i;
new Thread(() -> {
try {
Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(100));
System.out.println(index + "-thread finished");
countLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
countLatch.await(); // 阻塞,当计数器==0,就唤醒主线程往下执行。
System.out.println("main thread finished");
// 3-thread finished
// 4-thread finished
// 0-thread finished
// 1-thread finished
// 2-thread finished
// main thread finished
等待信号一起执行示例
初始化值为1的计数器,然后等待线程中通过await等待;主线程在合适的时候通过countDown来触发信号,让所有等待线程开始运行:
CountDownLatch countLatch = new CountDownLatch(1);
for (int i = 0; i < 5; i++) {
int index = i;
new Thread(() -> {
try {
// ready, wait for main-signal
countLatch.await();
System.out.println(index + "-thread to run");
Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(100));
System.out.println(index + "-thread finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(1000);
System.out.println("main thread send signal");
countLatch.countDown();
Thread.sleep(2000);
// main thread send signal
// 0-thread to run
// 2-thread to run
// 3-thread to run
// 1-thread to run
// 4-thread to run
//
// 1-thread finished
// 2-thread finished
// 4-thread finished
// 0-thread finished
// 3-thread finished
CyclicBarrier
在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。
主要方法说明
public CyclicBarrier(int parties)
public CyclicBarrier(int parties, Runnable barrierAction)
public int await()
public int await(long timeout, TimeUnit unit)
- parties 是参与线程的个数;
- Runnable 参数,表示最后一个到达的线程要做的任务;
示例
初始化与线程数量相等的Barrier,然后在每个线程进入后await:
CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("The last thread arrived barrier: " + Thread.currentThread().getName());
}
});
for (int i = 0; i < 5; i++) {
int index = i;
new Thread(() -> {
try {
Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(100));
System.out.println(index + "-thread come to barrier");
barrier.await();
System.out.println(index + "-thread to run");
Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(100));
System.out.println(index + "-thread finished");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(3000);
// 3-thread come to barrier
// 2-thread come to barrier
// 1-thread come to barrier
// 0-thread come to barrier
// 4-thread come to barrier
// The last thread arrived barrier: Thread-4
//
// 4-thread to run
// 0-thread to run
// 1-thread to run
// 2-thread to run
// 3-thread to run
//
// 0-thread finished
// 3-thread finished
// 1-thread finished
// 4-thread finished
// 2-thread finished