Java多线程编程--(10)学习Java5.0 并发编程包--线程工具类
上篇介绍了Semaphore 和 CyclicBarrier,这次说一下另外两个:CountDownLatch 和 Exchanger:
【CountDownLatch 】
倒计数器!用这个类,可以很好的模拟一个运动会场景:3个运动员1个裁判,运动员准备好后,裁判发令,然后运动员开跑,运动员结束后,告知裁判,等所有运动员都返回终点,裁判宣布比赛结果:
- package cn.test;
- import java.util.Random;
- import java.util.concurrent.Callable;
- import java.util.concurrent.CompletionService;
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.ExecutorCompletionService;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
- public class CountDownLatchTest {
- public static void main(String[] args) throws InterruptedException, ExecutionException {
- // 裁判的计数器,等待所有运动员准备好后,发令开跑
- final CountDownLatch refereeCountDownLatch = new CountDownLatch(1);
- // 运动员的计数器,一个3个运动员,每个运动员跑完后通过计数器报到,所有运动员都跑完后,裁判宣布成绩
- final CountDownLatch athletesCountDownLatch = new CountDownLatch(3);
- // 创建3个线程的线程池,作为3个待跑的运动员
- ExecutorService athletesThreadPool = Executors.newFixedThreadPool(3);
- // 因为我们最后去宣布成绩,所以我们希望运动员线程执行完毕后,返回一个结果,所以我们此处用CompletionService
- CompletionService<String> completionService = new ExecutorCompletionService<String>(athletesThreadPool);
- // 提交3个任务,作为3个运动员的跑步过程
- for(int i=0;i<3;i++){
- completionService.submit(new Callable<String>(){
- @Override
- public String call() throws Exception {
- System.out.println(Thread.currentThread().getName() + " 进入准备起跑阶段!");
- // 通知裁判
- refereeCountDownLatch.await();
- // 开始起跑,随即睡一段时间,来模拟跑步过程 new Random().nextDouble()返回0-1之间的数字
- long useTime = (long)(new Random().nextDouble() * 10000);
- Thread.sleep(useTime);
- System.out.println(Thread.currentThread().getName() + " 跑步结束!耗时:" + useTime);
- // 报告计数器,并且返回结果!
- athletesCountDownLatch.countDown();
- return "运动员:" + Thread.currentThread().getName();
- }});
- }
- // 主线程模拟裁判,先等上2秒钟,让运动员准备就绪(这里可以改进为当运动员准备好后,通知裁判!)
- Thread.sleep(2000);
- // 裁判宣布比赛可以
- System.out.println("裁判宣布比赛开始:");
- refereeCountDownLatch.countDown();
- // 裁判阻塞,等待结果
- athletesCountDownLatch.await();
- // 当所有运动员结束后,裁判宣布结果。CompletionService会按照执行完毕的顺序,依次返回线程执行结果
- Future<String> firstAthlete = completionService.take();
- Future<String> secondAthlete = completionService.take();
- Future<String> thridAthlete = completionService.take();
- System.out.println("本次运动会,冠军:" + firstAthlete.get());
- System.out.println("本次运动会,亚军:" + secondAthlete.get());
- System.out.println("本次运动会,季军:" + thridAthlete.get());
- }
- }
比赛结果为:
- pool-1-thread-1 进入准备起跑阶段!
- pool-1-thread-3 进入准备起跑阶段!
- pool-1-thread-2 进入准备起跑阶段!
- 裁判宣布比赛开始:
- pool-1-thread-3 跑步结束!耗时:8577
- pool-1-thread-1 跑步结束!耗时:8686
- pool-1-thread-2 跑步结束!耗时:9762
- 本次运动会,冠军:运动员:pool-1-thread-3
- 本次运动会,亚军:运动员:pool-1-thread-1
- 本次运动会,季军:运动员:pool-1-thread-2
创建CountDownLatch会接受一个整形参数,表示计数器初始值,调用对象的countDown()方法,会将这个初始值减1,直到0为止。线程调用对象的await()方法,如果计数器的值不为0,则阻塞,否则,该调用直接返回!我们上述过程,先让3个运动员线程全部阻塞在初始值为1的计数器上,裁判调用一下计数器的countDown()方法,3个运动员开始跑步。然后裁判await在一个初始值为3的计数器上,每个运动员结束后,调用一下这个计数器的countDown()方法,当所有运动员结束,裁判从计数器的await方法返回,宣布结果!这里我们还结合使用了ExecutorService 和 CompletionService,具体用法,可参考前面介绍!
【Exchanger】
线程变量交换器!这个可以实现两个线程在运行间,随时交换两个值!我们考虑一个例子,卖家和买家。卖家线程拿着货说我要卖,然后等待,买家线程来了,出钱,换得东西,两个线程继续进行:
- package cn.test;
- import java.util.concurrent.Exchanger;
- public class ExchangerTest {
- public static void main(String[] args) {
- final Exchanger<String> exchanger = new Exchanger<String>();
- // 先创建一个卖家线程,将东西准备好,开始卖
- new Thread(new Runnable(){
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName() + " : 卖东西了.....一手交钱一手交货!");
- try {
- String goods = "Good TV!";
- String getBack = exchanger.exchange(goods);
- System.out.println(Thread.currentThread().getName() + " : " + goods + " 卖出去了,得到了 " + getBack);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }}, "Seller").start();
- // 再创建一个买家线程,将钱准备好,开始买
- new Thread(new Runnable(){
- @Override
- public void run() {
- System.out.println(Thread.currentThread().getName() + " : 买东西了.....一手给货一手付钱!");
- try {
- String money = "2000$";
- String getBack = exchanger.exchange(money);
- System.out.println(Thread.currentThread().getName() + " : " + money + " 花了,得到了 " + getBack);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }}, "Buyer").start();
- }
- }
交易结果为:
- Seller : 卖东西了.....一手交钱一手交货!
- Buyer : 买东西了.....一手给货一手付钱!
- Buyer : 2000$ 花了,得到了 Good TV!
- Seller : Good TV! 卖出去了,得到了 2000$
我们看到两个线程互相交互了各自的一个局部变量!创建Exchanger对象,第一个线程调用对象的exchange方法(传递一个参数,即用于交换的变量),阻塞,等待另一个线程在这个对象上同样调用这个exhange方法。第二个线程调用后,两者互换调用参数,该方法返回!Exchanger是一个支持泛型的类,泛型就是用于交换的值的类型!
以上就是并发包中提到的4个好玩的线程控制工具类!大家了解一下即可,如果在实际中有这方面的需求,可以考虑能不能用这几个好玩的工具类来简化自己的线程控制代码!

浙公网安备 33010602011771号