Java并发编程(同步工具):CountDownLatch、CyclicBarrier、Semaphore区别及方法

一、CountDownLatch(线程计数器 )

CountDownLatch 类在 java.util.concurrent 包下,具有计数器的功能,等待其他线程执行完毕,主线程在继续执行,用于监听某些初始化操作,并且线程进行阻塞,等初始化执行完毕后,通知主线程继续工作执行

final CountDownLatch latch = new CountDownLatch(2);
	new Thread(){
		public void run() {
			 System.out.println("子线程1"+Thread.currentThread().getName()+"正在运行");
			 Thread.sleep(3000);
			 System.out.println("子线程1"+Thread.currentThread().getName()+"执行成功");
	 		 latch.countDown();
	};}.start();
	new Thread(){ 
		public void run() {
			 System.out.println("子线程2"+Thread.currentThread().getName()+"正在运行");
			 Thread.sleep(3000);
			 System.out.println("子线程2"+Thread.currentThread().getName()+"执行成功");
			 latch.countDown();
	};}.start();
	System.out.println("等待 2 个子线程的执行。。。");
	latch.await();
	System.out.println("2个子线程已经全部执行成功");
	System.out.println("继续执行主线程");
}

二、CyclicBarrier(回环栅栏-等待至 barrier 状态再全部同时执行)

回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环
是因为当所有等待线程都被释放以后,CyclicBarrier 可以被重用。被重用的时候叫做barrier,当调用 await()方法之后,线程就处于 barrier 了

CyclicBarrier 中的 await 方法

//用来挂起当前线程,直至所有线程都到达 barrier 状态再同时执行后续任务
public int await();
//让这些线程等待至一定的时间,如果还有线程没有到达 barrier 状态就直接让到达 barrier 的线程执行后续任务。
public int await(long timeout, TimeUnit unit);

使用代码如下:

public static void main(String[] args) {
	 int N = 4;
	 CyclicBarrier barrier = new CyclicBarrier(N);
	 for(int i=0;i<N;i++) new WriterTest(barrier).start();
 }
static class WriterTest extends Thread{
	 private CyclicBarrier clb;
	 
	 public WriterTest(CyclicBarrier clb) {
	 	this.clb = clb;
	 }
 
    @Override
	public void run() {
	 try {
	 		Thread.sleep(3000); 
			System.out.println(Thread.currentThread().getName()+"写入数据成功,正在等待其他线程的写入");
			clb.await();
	 } catch (InterruptedException e) {
		 e.printStackTrace();
	 }catch(BrokenBarrierException e){
	 	e.printStackTrace();
	 }
	 System.out.println("线程已全部写入,可以继续处理其他任务~~");
	 }
 }

三、Semaphore(信号量-控制同时访问的线程个数)

Semaphore 翻译成字面意思为 信号量,Semaphore 可以控制同时访问的线程个数,通过
acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可

Semaphore 类中比较重要的几个方法:

 //用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
 public void acquire();
 //获取 permits 个许可
 public void acquire(int permits);
 //释放许可。注意,在释放许可之前,必须先获获得许可。
 public void release() {} 
 //释放 permits 个许可
 public void release(int permits) {};

/*这四个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法*/

//尝试获取一个许可,若获取成功,则立即返回 true,若获取失败,则立即返回 false
public boolean tryAcquire();
//尝试获取一个许可,若在指定的时间内获取成功,则立即返回 true,否则立即返回 false
public boolean tryAcquire(long timeout, TimeUnit unit);
//尝试获取 permits 个许可,若获取成功,则立即返回 true,若获取失败,则立即返回 false
public boolean tryAcquire(int permits);
// 尝试获取 permits个许可,若在指定的时间内获取成功,则立即返回 true,否则则立即返回 false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit);
//还可以通过 availablePermits()方法得到可用的许可数目。

若一个工厂有 5 台机器,但是有 8 个工人,一台机器同时只能被一个工人使用,只有使用完
了,其他工人才能继续使用。那么我们就可以通过 Semaphore 来实现

static class WorkerTest extends Thread{
	private int num;
	private Semaphore semaphore;
	public WorkerTest(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();
		} 
	}
}
public static void main(String[] args) {
	 int N = 8; //工人数
	Semaphore semaphore = new Semaphore(5); //机器数目
	for(int i=0;i<N;i++) new WorkerTest(i,semaphore).start();
 }

方法小结

CyclicBarrier和CountDownLatch的区别

countDownLatchCyclicBarrier
减计数方式加计数方式
计算为0时释放所有等待的线程计数达到指定值时释放所有等待线程
计数为0时,无法重置计数达到指定值时,计数置为0重新开始
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用可重复利用
  • CountDownLanch是为计数器是设置一个值,当多次执行countdown后,计数器减为0的时候所有线程被唤醒,然后CountDownLanch失效,只能够使用一次
  • CyclicBarrier是当count为0时同样唤醒全部线程,同时会重新设置count为parties,重新new一个generation来实现重复利用
  • CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行
  • CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行

CyclicBarrier 小结

CyclicBarrier采用一个内部类Generation来维护当前循环,每一个await方法都会存储当前的generation,获取到相同generation对象的属于同一组,每当count的次数耗尽就会重新new一个Generation并且重新设置count的值为parties,表示进入下一次新的循环。 从这个await方法我们是不是可以知道只要有一个线程被中断了,当代的 generation的broken 就会被设置为true,所以会导致其他的线程也会被抛出BrokenBarrierException。相当于一个失败其他也必须失败,感觉有“强一致性“的味道

Semaphore 工作机制

信号量就是就是用来保证两个或多个关键代码段(公共资源)不被并发调用,就像火车上的厕所一样,不允许多余1个人同时使用,厕所就相当于公共资源,当一个人进入厕所后,该公共资源(厕所)即被占用,除非那个人从厕所出来(释放公共资源),别的人(进程)才可以使用厕所(公共资源)

信号量是一个非负整数,所有通过它的进程、线程都会将该整数减1,当该整数值为0时,所有试图通过它的进程都将处于等待状态,在信号量上我们定义两种操作,wait(个人理解为申请比较好)和Release(释放),当一个进程调用wait操作时,它要么得到资源后将信号量减1,要么一直等下去(放入阻塞队列),直到信号量大于等于1时

简单的流程示意图
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值