多线程学习-day-07CountDownLatch、CyclicBarrier

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Xgx120413/article/details/83384789

线程基础、线程之间的共享和协作

(目前会将一些概念简单描述,一些重点的点会详细描述)

学习目标:多线程的并发工具类(3)

CountDownLatch、CyclicBarrier

一、CountDownLatch

官方介绍:

CountDownLatch是在java1.5被引入的,它存在于java.util.concurrent包下。CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

什么意思呢?就是运行过程中,有几个线程,那么只有当几个线程同时就绪之后,类似于100米赛跑一样,必须所有运动员到达起跑线之后,通过一个发令枪才能够一起冲向终点,运行起来。

里面就有几个方法,分别介绍一下:

countDown()方法:该方法初始值设置为允许运行的线程数,这里比如赛道上只能容纳10个人,则初始值为10,然后每一次线程执行完,则将初始值10减1,一直减到0为止,然后表示所有的运动员都就位了,然后就等待发令枪声响就开始同时运行了。这里要注意的点是,必须在每一个线程执行完之后,调用countDown()方法,否则数据将出错!

await()方法:该方法就相当于发令枪,当判断countDown()将初始值一直减到0 之后,表示所有的线程已经就绪了,就执行await()方法,所有线程就开始同时执行后续操作。

下面来看代码:

import java.util.concurrent.CountDownLatch;

import com.xiangxue.tools.SleepTools;

/**
 * CountDownLatch工具类使用
 * 
 * @author xgx
 */
public class UseCountDownLatch {

	// 定义总共有7个球
	private static final int ALL_SEVEN_BALL = 7;

	// 实例化CountDownLatch类
	private static CountDownLatch countDownLatch = new CountDownLatch(ALL_SEVEN_BALL);

	/**
	 * 调用CountDownLatch实例
	 * 
	 * @param args
	 * @throws InterruptedException
	 */
	public static void main(String[] args) throws InterruptedException {
		for (int i = 1; i <= ALL_SEVEN_BALL; i++) {
			Thread thread = new Thread(new SubCountDownLatch());
			thread.start();
		}
		// 当所有线程已经执行完毕,调用await()方法,将同时线程置于非阻塞状态,执行后续的业务
		countDownLatch.await();
		System.out.println("已经成功集齐7色球!恭喜中大奖!");
	}

	// 定义CountDownLatch多线程类,表示这个类要做什么事情
	private static class SubCountDownLatch implements Runnable {

		// 所有7个线程countDown()方法没有把总数减为0时,都将阻塞,当通过countDown()方法将数量减到0时,则所有的线程都已经执行完毕,进行输出
		public void run() {
			// 这里输出我意思了一下。至于为什么Thread.currentThread().getId()是从10开始的,自行百度,这里不多说。
			System.out.println("已经搜集到了第: " + (Long.valueOf(Thread.currentThread().getId()) - 9) + " 个球");
			SleepTools.ms(1000);
			countDownLatch.countDown();
		}
	}
}

控制台输出结果:
已经搜集到了第: 6 个球
已经搜集到了第: 5 个球
已经搜集到了第: 4 个球
已经搜集到了第: 2 个球
已经搜集到了第: 7 个球
已经搜集到了第: 3 个球
已经搜集到了第: 1 个球
已经成功集齐7色球!恭喜中大奖!

我们看到,所有线程都是按照顺序来执行的,没有出现线程不安全结果。

二、CyclicBarrier

官方介绍:

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到所有线程都到达某个公共屏障点(也可以叫同步点),即相互等待的线程都完成调用await方法,所有被屏障拦截的线程才会继续运行await方法后面的程序。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该屏障点在释放等待线程后可以重用,所以称它为循环的屏障点。CyclicBarrier支持一个可选的Runnable命令,在一组线程中的最后一个线程到达屏障点之后(但在释放所有线程之前),该命令只在所有线程到达屏障点之后运行一次,并且该命令由最后一个进入屏障点的线程执行。

什么意思呢?CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。还是举上面的例子,这次就是得7个人,然后去买cai票,然后等最后一个买完,7个人都到一起之后(这里说到达屏障点之后),就可以运行后续的程序了。

来看实现代码:

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier工具类使用
 * 
 * @author xgx
 */
public class UseCyclicBarrier {

	// 定义总共有7个球
	private static final int ALL_SEVEN_BALL = 7;

	// 定义CyclicBarrier对象
	private static CyclicBarrier cyclicBarrier = new CyclicBarrier(ALL_SEVEN_BALL, new ResultCyclicBarrier());

	// 主方法
	public static void main(String[] args) {
		for (int i = 0; i < ALL_SEVEN_BALL; i++) {
			Thread thread = new Thread(new OperateCyclicBarrier());
			thread.start();
		}
	}

	// 定义一个最终结果的类
	private static class ResultCyclicBarrier implements Runnable {
		public void run() {
			// 调用操作类,然后都在屏障点等候
			new OperateCyclicBarrier();
			// 当ALL_SEVEN_BALL个人都已经成功操作完成之后,在执行后续业务
			System.out.println("已经成功集齐7色球!恭喜中大奖!");
		}
	}

	// 定义一个ALL_SEVEN_BALL个数线程操作类
	private static class OperateCyclicBarrier implements Runnable {
		// 做ALL_SEVEN_BALL个数的事情
		public void run() {
			try {
				System.out.println("第:" + Long.valueOf(Thread.currentThread().getId() - 9) + " 个人开始出发收集七色球!!!");
				Thread.sleep(new Random().nextInt(3000));
				// 所有的线程全部到达屏障点之前,都处于阻塞状态
				cyclicBarrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}
}

控制台输出结果:
第:2 个人开始出发收集七色球!!!
第:6 个人开始出发收集七色球!!!
第:3 个人开始出发收集七色球!!!
第:1 个人开始出发收集七色球!!!
第:4 个人开始出发收集七色球!!!
第:7 个人开始出发收集七色球!!!
第:5 个人开始出发收集七色球!!!
已经成功集齐7色球!恭喜中大奖!

三、CountDownLatch和CyclicBarrier的区别

(1)CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。(这里CyclicBarrier更多的方法这里后兴趣的朋友可以自行研究)

(2)CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。

(3)CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

(4)CountDownLatch依靠一个外力(计数器,发令枪)来控制线程,而CyclicBarrier是相当于本身来控制线程,举个例子:

        CountDownLatch:有个装满宝石的房间,门外有7把锁,然后有7个人要进入房间组成队伍A,还有另外7个人手里拿着钥匙组成队伍B,那么首先的A组7人必须等到B组7人把钥匙送过来,然后把门外7把锁给分别打开后,这A组7人才能够进入房间拿到宝石。

        CyclicBarrier:有个装满宝石的房间,门外也有7把锁,然后这7个人必须到另外一个房间,各自完成一个任务,然后每个人才能够获得一把锁,完成任务之后,这7个人就能够打开那7把锁,然后拿到宝石。

因为时间有限,每天晚上只能总结一点,因此希望能够理解思想,继续学习进步!!

猜你喜欢

转载自blog.csdn.net/Xgx120413/article/details/83384789