Java多线程中对CountDownLatch的使用

时间:2023-03-10 02:07:21
Java多线程中对CountDownLatch的使用

1、CountDownLatch和CyclicBarrier作用

CountDownLatch和CyclicBarrier都位于java.util.concurrent包中,都具有计数功能,一般用于多线程间的协作。

CountDownLatch是减法计数器,子线程中调用countDown()计数减1,主线程调用await()阻塞线程执行,直到计数为0才会让线程继续执行,CountDownLatch不可重复利用。

CyclicBarrier是加法计数器,子线程调用await()计数加1,同时阻塞线程执行,直到兄弟线程都执行了await(),计数到达了设定值,CyclicBarrier可重复使用,计数到达指定值后重新开始。CyclicBarrier构造函数设定一个Runnable参数,通知计数已到达指定值。

2、使用示例

package com.zhi.test;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier; import org.junit.Test; /**
* CyclicBarrier和CountDownLatch使用样例
*
* @author 张远志
* @since 2020年5月4日18:01:00
*
*/
public class MyTest {
@Test
public void test() {
int jobCount = 5;
CountDownLatch latch = new CountDownLatch(jobCount);
CyclicBarrier barrier = new CyclicBarrier(jobCount, new Runnable() {
public void run() {
System.out.println("所有线程都到了");
}
});
for (int i = 1; i <= jobCount; i++) {
new Thread(new Job(latch, barrier, i)).start();
}
try {
latch.await();
} catch (Exception e) {
}
System.out.println("所有线程已执行完成!");
} class Job implements Runnable {
private CountDownLatch latch;
private CyclicBarrier barrier;
private final int id; public Job(CountDownLatch latch, CyclicBarrier barrier, int id) {
this.latch = latch;
this.barrier = barrier;
this.id = id;
} @Override
public void run() {
try {
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("线程" + id + "到达栅栏1");
barrier.await();
} catch (Exception e) {
}
try {
System.out.println("线程" + id + "到达栅栏2");
barrier.await();
} catch (Exception e) {
}
System.out.println("任务" + id + "执行完成");
latch.countDown();
}
}
}

执行结果:

线程2到达栅栏1
线程4到达栅栏1
线程5到达栅栏1
线程3到达栅栏1
线程1到达栅栏1
所有线程都到了
线程1到达栅栏2
线程2到达栅栏2
线程5到达栅栏2
线程4到达栅栏2
线程3到达栅栏2
所有线程都到了
任务3执行完成
任务1执行完成
任务2执行完成
任务4执行完成
任务5执行完成
所有线程已执行完成!

3、如何保证计数的线程安全性

CountDownLatch的countDown会进行加法计数,CyclicBarrier的await()会进行加法计数,查看源码我们会发现,这2个方法都没有使用synchronized关键字,那他们有时如何保证线程安全性的呢?

查看CountDownLatch源码,我们看到CountDownLatch使用了Sync内部类(继承AbstractQueuedSynchronizer)做减法,通过Unsafe类比较并设置新值。

Java多线程中对CountDownLatch的使用

Java多线程中对CountDownLatch的使用

查看CyclicBarrier源码,我们看到ReentrantLock锁。为啥它与CountDownLatch不一样呢,因为CyclicBarrier不仅要完成计数,还需要调用barrierCommand,并且要实现重复可用(将count值还原),其实ReentrantLock内部也使用Unsafe类进行了原子操作。

Java多线程中对CountDownLatch的使用