CountDownLatch&CyclicBarrier&Semaphore
- CountDownLatch和CyclicBarrier都能够实现线程之间的等待,但侧重点略微不同
- CountDownLatch一般用于某个线程等待若干个其他线程执行完任务之后它才执行
- CyclicBarrier一般用于一组线程相互等待至某个状态,然后这一组线程再同时执行
- 而且CountDownLatch是使用AQS框架实现的,而CyclicBarrier使用ReentrantLock实现的
- Semaphore和锁有点类似,它一般用于控制对一组资源的访问权限,它也是使用AQS框架实现的,并且内部有两种模式,一种是FairSync,代表让先进入队列中的等待任务获取一个许可,第二种是NonFairSync,代表不管是不是先进入队列中的,可以直接去获取许可
CountDownLatch
-
CountDownLatch是在java1.5被引入的,跟它一起引入的工具类还有CyclicBarrier、Semaphore、ConcurrentHashmap和BlockingQueue
-
CountDownLathc使一个线程等待其他线程各自执行完毕后再执行
-
它是通过一个计数器实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后再闭锁上等待的线程就可以恢复工作了。
源码分析
- CountDownLatch类只提供了一个构造器
//参数count为计数值
//CountDownLatch也是基于AQS框架的,这里的count其实就是赋给AQS的state
public CountDownLatch(int count) { };
- 类中有三个方法很重要
//调用await方法的线程会被挂起,它会等待直到count值为0时才继续执行
public void await() throws InterruptedException{};
//和await方法类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout,TimeUnit unit) throws InterruptedException{};
//count-1
public void countDown(){};
- 普通示例
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(2);
System.out.println("主线程开始执行…… ……");
//第一个子线程执行
EXECUTOR_SERVICE.execute(()->{
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
//第二个子线程执行
EXECUTOR_SERVICE.execute(()->{
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
System.out.println("等待两个线程执行完毕…… ……");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个子线程都执行完毕,继续执行主线程");
}
- 结果如下:
主线程开始执行…… ……
等待两个线程执行完毕…… ……
子线程:mycountdownlatch-pool-0执行
子线程:mycountdownlatch-pool-1执行
两个子线程都执行完毕,继续执行主线程
- 并发示例
public class MyCountDownLatch {
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60,TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("mycountdownlatch-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(100);
for(int i=0;i<100;i++){
CountRunnable runnable = new CountRunnable(latch);
EXECUTOR_SERVICE.execute(runnable);
}
}
}
class CountRunnable implements Runnable{
private CountDownLatch latch = new CountDownLatch(100);
public CountRunnable(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try{
synchronized (latch){
latch.countDown();
System.out.println("thread counts = " + (latch.getCount()));
}
latch.await();
System.out.println("concurrency counts = " + (100 - latch.getCount()));
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
CyclicBarrier
- 中文意思是回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。当调用await方法之后,线程就处于barrier了。
源码分析
//parties指让多少个线程或者任务等待值barrier状态
//参数BarrierAction为这些线程都达到barrier状态时会执行的内容
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
//挂起当前线程,直到所有线程都达到barrier状态再同时执行后续任务
public int await() throws InterruptedException, BrokenBarrierException { };
//让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
示例代码
- 示例代码如下
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("mycyclicbarrier-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(4);
for(int i=0;i<4;i++){
EXECUTOR_SERVICE.execute(()->{
System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
try {
Thread.sleep(5000); //以睡眠来模拟写入数据操作
System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有线程写入完毕,继续处理其他任务...");
});
}
}
- 结果如下
线程mycyclicbarrier-pool-0正在写入数据...
线程mycyclicbarrier-pool-3正在写入数据...
线程mycyclicbarrier-pool-2正在写入数据...
线程mycyclicbarrier-pool-1正在写入数据...
线程mycyclicbarrier-pool-0写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-2写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-3写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
Semaphore
- 字面意思信号量,Semaphore可以控制同时访问的线程个数,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可
源码分析
public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
//获取一个许可,若无许可则会一直等待,直到获取许可
public void acquire() throws InterruptedException { }
//获取permits许可
public void acquire(int permits) throws InterruptedException { }
//释放一个许可
public void release() { }
//释放permits个许可
public void release(int permits) { }
示例代码
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("mycyclicbarrier-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for(int i=0;i<8;i++){
EXECUTOR_SERVICE.execute(new SemaphoreRunnable(i,semaphore));
}
}
class SemaphoreRunnable implements Runnable{
int num;
Semaphore semaphore;
public SemaphoreRunnable(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"占用一个机器在生产...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"释放出机器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 结果如下:
工人0占用一个机器在生产...
工人1占用一个机器在生产...
工人2占用一个机器在生产...
工人3占用一个机器在生产...
工人4占用一个机器在生产...
工人1释放出机器
工人4释放出机器
工人5占用一个机器在生产...
工人6占用一个机器在生产...
工人0释放出机器
工人2释放出机器
工人3释放出机器
工人7占用一个机器在生产...
工人7释放出机器
工人5释放出机器
工人6释放出机器