Java多线程编程--(10)学习Java5.0 并发编程包--线程工具类

上篇介绍了Semaphore 和 CyclicBarrier,这次说一下另外两个:CountDownLatch 和 Exchanger:

【CountDownLatch 】

倒计数器!用这个类,可以很好的模拟一个运动会场景:3个运动员1个裁判,运动员准备好后,裁判发令,然后运动员开跑,运动员结束后,告知裁判,等所有运动员都返回终点,裁判宣布比赛结果:

[java] view plain copy
 
  1. package cn.test;  
  2.   
  3. import java.util.Random;  
  4. import java.util.concurrent.Callable;  
  5. import java.util.concurrent.CompletionService;  
  6. import java.util.concurrent.CountDownLatch;  
  7. import java.util.concurrent.ExecutionException;  
  8. import java.util.concurrent.ExecutorCompletionService;  
  9. import java.util.concurrent.ExecutorService;  
  10. import java.util.concurrent.Executors;  
  11. import java.util.concurrent.Future;  
  12.   
  13. public class CountDownLatchTest {  
  14.   
  15.     public static void main(String[] args) throws InterruptedException, ExecutionException {  
  16.           
  17.         // 裁判的计数器,等待所有运动员准备好后,发令开跑  
  18.         final CountDownLatch refereeCountDownLatch = new CountDownLatch(1);  
  19.         // 运动员的计数器,一个3个运动员,每个运动员跑完后通过计数器报到,所有运动员都跑完后,裁判宣布成绩  
  20.         final CountDownLatch athletesCountDownLatch = new CountDownLatch(3);  
  21.         // 创建3个线程的线程池,作为3个待跑的运动员  
  22.         ExecutorService athletesThreadPool = Executors.newFixedThreadPool(3);  
  23.         // 因为我们最后去宣布成绩,所以我们希望运动员线程执行完毕后,返回一个结果,所以我们此处用CompletionService  
  24.         CompletionService<String> completionService = new ExecutorCompletionService<String>(athletesThreadPool);  
  25.         // 提交3个任务,作为3个运动员的跑步过程  
  26.         for(int i=0;i<3;i++){  
  27.               
  28.             completionService.submit(new Callable<String>(){  
  29.   
  30.                 @Override  
  31.                 public String call() throws Exception {  
  32.                       
  33.                     System.out.println(Thread.currentThread().getName() + " 进入准备起跑阶段!");  
  34.                     // 通知裁判  
  35.                     refereeCountDownLatch.await();  
  36.                     // 开始起跑,随即睡一段时间,来模拟跑步过程 new Random().nextDouble()返回0-1之间的数字  
  37.                     long useTime = (long)(new Random().nextDouble() * 10000);  
  38.                     Thread.sleep(useTime);  
  39.                     System.out.println(Thread.currentThread().getName() + " 跑步结束!耗时:" + useTime);  
  40.                     // 报告计数器,并且返回结果!  
  41.                     athletesCountDownLatch.countDown();  
  42.                     return "运动员:" + Thread.currentThread().getName();  
  43.                 }});  
  44.         }  
  45.         // 主线程模拟裁判,先等上2秒钟,让运动员准备就绪(这里可以改进为当运动员准备好后,通知裁判!)  
  46.         Thread.sleep(2000);  
  47.         // 裁判宣布比赛可以  
  48.         System.out.println("裁判宣布比赛开始:");  
  49.         refereeCountDownLatch.countDown();  
  50.         // 裁判阻塞,等待结果  
  51.         athletesCountDownLatch.await();  
  52.         // 当所有运动员结束后,裁判宣布结果。CompletionService会按照执行完毕的顺序,依次返回线程执行结果  
  53.         Future<String> firstAthlete = completionService.take();  
  54.         Future<String> secondAthlete = completionService.take();  
  55.         Future<String> thridAthlete = completionService.take();  
  56.           
  57.         System.out.println("本次运动会,冠军:" + firstAthlete.get());  
  58.         System.out.println("本次运动会,亚军:" + secondAthlete.get());  
  59.         System.out.println("本次运动会,季军:" + thridAthlete.get());  
  60.           
  61.                   
  62.     }  
  63.   
  64. }  

比赛结果为:

[html] view plain copy
 
  1. pool-1-thread-1 进入准备起跑阶段!  
  2. pool-1-thread-3 进入准备起跑阶段!  
  3. pool-1-thread-2 进入准备起跑阶段!  
  4. 裁判宣布比赛开始:  
  5. pool-1-thread-3 跑步结束!耗时:8577  
  6. pool-1-thread-1 跑步结束!耗时:8686  
  7. pool-1-thread-2 跑步结束!耗时:9762  
  8. 本次运动会,冠军:运动员:pool-1-thread-3  
  9. 本次运动会,亚军:运动员:pool-1-thread-1  
  10. 本次运动会,季军:运动员:pool-1-thread-2  

创建CountDownLatch会接受一个整形参数,表示计数器初始值,调用对象的countDown()方法,会将这个初始值减1,直到0为止。线程调用对象的await()方法,如果计数器的值不为0,则阻塞,否则,该调用直接返回!我们上述过程,先让3个运动员线程全部阻塞在初始值为1的计数器上,裁判调用一下计数器的countDown()方法,3个运动员开始跑步。然后裁判await在一个初始值为3的计数器上,每个运动员结束后,调用一下这个计数器的countDown()方法,当所有运动员结束,裁判从计数器的await方法返回,宣布结果!这里我们还结合使用了ExecutorService 和 CompletionService,具体用法,可参考前面介绍!

【Exchanger】

线程变量交换器!这个可以实现两个线程在运行间,随时交换两个值!我们考虑一个例子,卖家和买家。卖家线程拿着货说我要卖,然后等待,买家线程来了,出钱,换得东西,两个线程继续进行:

[java] view plain copy
 
  1. package cn.test;  
  2.   
  3. import java.util.concurrent.Exchanger;  
  4.   
  5. public class ExchangerTest {  
  6.   
  7.     public static void main(String[] args) {  
  8.           
  9.         final Exchanger<String> exchanger = new Exchanger<String>();  
  10.         // 先创建一个卖家线程,将东西准备好,开始卖  
  11.         new Thread(new Runnable(){  
  12.   
  13.             @Override  
  14.             public void run() {  
  15.                   
  16.                 System.out.println(Thread.currentThread().getName() + " : 卖东西了.....一手交钱一手交货!");  
  17.                 try {  
  18.                     String goods = "Good TV!";  
  19.                     String getBack = exchanger.exchange(goods);  
  20.                     System.out.println(Thread.currentThread().getName() + " : " + goods + " 卖出去了,得到了 " + getBack);  
  21.                 } catch (InterruptedException e) {  
  22.                     e.printStackTrace();  
  23.                 }  
  24.                   
  25.             }}, "Seller").start();  
  26.           
  27.         // 再创建一个买家线程,将钱准备好,开始买  
  28.         new Thread(new Runnable(){  
  29.   
  30.             @Override  
  31.             public void run() {  
  32.                   
  33.                 System.out.println(Thread.currentThread().getName() + " : 买东西了.....一手给货一手付钱!");  
  34.                 try {  
  35.                     String money = "2000$";  
  36.                     String getBack = exchanger.exchange(money);  
  37.                     System.out.println(Thread.currentThread().getName()  + " : " + money + " 花了,得到了 " + getBack);  
  38.                 } catch (InterruptedException e) {  
  39.                     e.printStackTrace();  
  40.                 }  
  41.                   
  42.             }}, "Buyer").start();  
  43.     }  
  44.   
  45. }  


交易结果为:

[html] view plain copy
 
  1. Seller : 卖东西了.....一手交钱一手交货!  
  2. Buyer : 买东西了.....一手给货一手付钱!  
  3. Buyer : 2000$ 花了,得到了 Good TV!  
  4. Seller : Good TV! 卖出去了,得到了 2000$  

我们看到两个线程互相交互了各自的一个局部变量!创建Exchanger对象,第一个线程调用对象的exchange方法(传递一个参数,即用于交换的变量),阻塞,等待另一个线程在这个对象上同样调用这个exhange方法。第二个线程调用后,两者互换调用参数,该方法返回!Exchanger是一个支持泛型的类,泛型就是用于交换的值的类型!

 

以上就是并发包中提到的4个好玩的线程控制工具类!大家了解一下即可,如果在实际中有这方面的需求,可以考虑能不能用这几个好玩的工具类来简化自己的线程控制代码!

posted @ 2016-12-02 15:30  天涯海角路  阅读(152)  评论(0)    收藏  举报