【Java并发】CountDownLatch+CyclicBarrier+Semaphore:样例入门篇

1 CountDownLatch

倒计时门闩:[ˈkaʊntdaʊn] [lætʃ],顾名思义,门闩有多个,当门闩全部开启时,门闩数减为0,才能把门打开,在门前等待的人才能出去。

来个例子:在线程池中,并发执行10个任务,主线程观察任务执行情况,并等待任务全部执行结束,输出执行结果。

// 创建线程池,核心池-0,最大-10,timeout-0,队列-实时队列
// 当有任务来的时候,会立即创建线程处理,如果任务结束,线程立即销毁
ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 10, 0L, TimeUnit.SECONDS, new SynchronousQueue<>());

// 初始化CountDownLatch,大小为10
CountDownLatch countDownLatch = new CountDownLatch(10);

for (int i = 0; i < 10; i++) {
    // 往线程池提交任务
    executor.execute(new Runnable() {
	@Override
	public void run() {
	    try {
		// 模拟耗时
		Thread.sleep(Long.valueOf(String.valueOf(new Double(Math.random() * 1000).intValue())));
	    } catch (InterruptedException e) {
		e.printStackTrace();
	    }
	    System.out.println("over");
	    // 子线程完成,countDownLatch执行countDown
	    countDownLatch.countDown();
	}
    });
    // 打印线程池运行状态
    System.out.println(
	    "正在执行的任务数:" + executor.getPoolSize() + ",已执行结束的任务数:" + executor.getCompletedTaskCount());
}
//观测5次
int n = 5;
while (n > 0) {
    try {
	TimeUnit.MILLISECONDS.sleep(60);
    } catch (InterruptedException e) {
	e.printStackTrace();
    }
    n--;
    System.out.println(
	    "观测:正在执行的任务数:" + executor.getPoolSize() + ",已执行结束的任务数:" + executor.getCompletedTaskCount());
}
// 阻塞当前线程,直到所有子线程都执行countDown方法才会继续执行
try {
    countDownLatch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

// 打印线程池运行状态
System.out.println("最终结果:正在执行的任务数:" + executor.getPoolSize() + ",已执行结束的任务数:" + executor.getCompletedTaskCount());
// 停止运行线程池
executor.shutdown();

结果:

  • 第一阶段:任务队列依次添加到线程池中
  • 第二阶段:任务依次执行结束,观测期间执行完成4个任务
  • 第三阶段:主线程等待所有子线程执行结束,await等待,当所有countDown都运行完成,主线程输出最终结果
正在执行的任务数:1,已执行结束的任务数:0
正在执行的任务数:2,已执行结束的任务数:0
正在执行的任务数:3,已执行结束的任务数:0
正在执行的任务数:4,已执行结束的任务数:0
正在执行的任务数:5,已执行结束的任务数:0
正在执行的任务数:6,已执行结束的任务数:0
正在执行的任务数:7,已执行结束的任务数:0
正在执行的任务数:8,已执行结束的任务数:0
正在执行的任务数:9,已执行结束的任务数:0
正在执行的任务数:10,已执行结束的任务数:0
over
over
观测:正在执行的任务数:8,已执行结束的任务数:2
观测:正在执行的任务数:8,已执行结束的任务数:2
观测:正在执行的任务数:8,已执行结束的任务数:2
over
观测:正在执行的任务数:7,已执行结束的任务数:3
over
观测:正在执行的任务数:6,已执行结束的任务数:4
over
over
over
over
over
over
最终结果:正在执行的任务数:0,已执行结束的任务数:10

注意点:

CountDownLatch在对任务执行完成,进行判断时,要注意异常考虑,确保所有线程都可以执行countDown,即确保CountDownLatch可以归0,不然可能造成await线程无限期阻塞,如果无法确保,则await线程采用超时等待机制,如:await(long timeout, TimeUnit unit)。

2 CyclicBarrier

循环障碍:['saiklik]['bæriər],顾名思义,障碍只有一个,当障碍前人数达到一定量才会打开,且障碍可以循环使用,打开之后,放走一波人,立即关上,等待第二次使用。

来个例子:一群好朋友去徒步旅行,等人到齐了,一起出发,中途大家进行休息,等人全休息好了,再重新启程。

int total = 5;
// 创建线程池,核心池-0,最大-5,timeout-0,队列-实时队列
// 当有任务来的时候,会立即创建线程处理,如果任务结束,线程立即销毁
ThreadPoolExecutor executor = new ThreadPoolExecutor(0, total, 0L, TimeUnit.SECONDS, new SynchronousQueue<>());

// 初始化CyclicBarrier,大小为5
CyclicBarrier cyclicBarrier = new CyclicBarrier(total);

for (int i = 0; i < total; i++) {
    // 往线程池提交任务
    executor.execute(new Runnable() {
	@Override
	public void run() {
	    try {
		// 模拟随机等待
		Thread.sleep(Long.valueOf(String.valueOf(new Double(Math.random() * 1000).intValue())));
		System.out.println("Waiting for everyone to be ready,left number:"+(total-cyclicBarrier.getNumberWaiting()));
		// 等待队友就位
		cyclicBarrier.await();
		System.out.println("Lat's go");
		// 模拟随机等待
		Thread.sleep(Long.valueOf(String.valueOf(new Double(Math.random() * 1000).intValue())));
		System.out.println("Waiting for everyone to have a good rest,left number:"+(total-cyclicBarrier.getNumberWaiting()));
		// 等待队友休息结束
		cyclicBarrier.await();
		System.out.println("Lat's go again");
	    } catch (InterruptedException e) {
		e.printStackTrace();
	    } catch (BrokenBarrierException e) {
		e.printStackTrace();
	    }
	}
    });
}
// 停止运行线程池
executor.shutdown();

结果:一目了然!

Waiting for everyone to be ready,left number:5
Waiting for everyone to be ready,left number:4
Waiting for everyone to be ready,left number:3
Waiting for everyone to be ready,left number:2
Waiting for everyone to be ready,left number:1
Lat's go
Lat's go
Lat's go
Lat's go
Lat's go
Waiting for everyone to have a good rest,left number:5
Waiting for everyone to have a good rest,left number:4
Waiting for everyone to have a good rest,left number:3
Waiting for everyone to have a good rest,left number:2
Waiting for everyone to have a good rest,left number:1
Lat's go again
Lat's go again
Lat's go again
Lat's go again
Lat's go again

其他知识点介绍:

  • await(timeout,TimeUnit):当等待超时,会抛出TimeoutException异常,并停止等待
  • isBroken():获取是否破损标志位broken的值:CyclicBarrier初始化时或调用CyclicBarrier.reset()方法后,则broken=false;如果等待线程被中断或超时,则broken=true
  • reset():重置CyclicBarrier,是否破损标志位broken置为false,之前等待的,会抛出BrokenBarrierException异常
  • BrokenBarrierException:当某等待线程(A)被中断,或等待超时,或线程(A)调用CyclicBarrier.reset(),那么除线程(A)之外的其他等待线程都会触发该异常

PS:CyclicBarrier与CountDownLatch,功能上其实可以相通,CyclicBarrier也就多了一个循环使用的概念。

3 Semaphore

信号量:['seməfɔːr],顾名思义,就是个信号!但是它有量!只有拿到信号的才能通过,不然会被阻拦,已经获取信号的可以释放,这样就可以把机会让给其他人。

public class SemaphoreTest {

    public static void main(String[] args) {
	int total = 4;
	ThreadPoolExecutor executor = new ThreadPoolExecutor(0, total, 0L, TimeUnit.SECONDS, new SynchronousQueue<>());
	//池子大小为2
	final Pool pool = new Pool(2);
	Runnable worker = new Runnable() {
	    public void run() {
		try {
		    pool.in(1);//一人入场
		    // 模拟耗时
		    Thread.sleep(Long.valueOf(String.valueOf(new Double(Math.random() * 1000).intValue())));
		    pool.out(1);//一人出场
		} catch (InterruptedException ex) {
		}

	    }
	};
	for (int i = 0; i < total; i++) {
	    executor.submit(worker);
	}
	executor.shutdown();
    }
}

/**
 * 模拟资源池
 */
class Pool {

    Semaphore pass = null;

    public Pool(int size) {
	pass = new Semaphore(size);
    }

    public void in(int num) throws InterruptedException {
	// 获取信号量才能入场
	System.out.println("Try to get a pass...,left:"+pass.availablePermits());
	pass.acquire(num);
	//入场
	System.out.println("Got a pass,I'm in...");
    }

    public void out(int num) {
	// 归还信号量,出场
	System.out.println("Release a pass,I'm out...");
	pass.release(num);
    }

}

结果:先是两个人依次入场,入场券减少到0,之后入场的等待入场券,无法入场,当前两个退出后,后两个才入场成功

Try to get a pass...,left:2
Got a pass,I'm in...
Try to get a pass...,left:1
Got a pass,I'm in...
Try to get a pass...,left:0
Try to get a pass...,left:0
Release a pass,I'm out...
Got a pass,I'm in...
Release a pass,I'm out...
Got a pass,I'm in...
Release a pass,I'm out...
Release a pass,I'm out...

PS:Semaphore相当于允许固定并发访问的synchronized,而且每次入场消耗还可以控制,程序中为 1,也可以改为 2哦

 

 


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqchaozai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值