Java分布式应用学习笔记05多线程下的并发同步器----后篇

本文主要是介绍Java分布式应用学习笔记05多线程下的并发同步器----后篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

5.  CountDownLatch

很多资料上都说CountDownLatch是倒数计数器,我觉得这种说法太过专业,其实它就是一个数数的人员。利用它,可以在多线程执行任务完毕后完成进行多线程的等待,便于等待所有的线程之后在干别的事情,这个有点类似于FutureTask,使用上不太一样。这个场景就是一个线程必须要等到其他线程执行完毕后才能往下执行,注意,这里这个线程没必要需要其他线程的执行结果,而是只有一个条件就是其他线程必须执行完毕。咱们依然做个比喻,韦小宝需要8部《四十二章经》才能去鹿鼎山找寻宝藏,怎么办,他有7个老婆,不能资源浪费啊,这个任务同时进行吧。咱们设计8个线程同时进行,等所有老婆都执行完毕了,每个老婆找齐了《四十二章经》了,好了,他可以自己去找宝藏了。等等,咱们小宝哥有7个老婆,何来8个线程,这个问题,读者不必较真,举个例子罢了,咱们给他加个老婆不就行了!

代码如下

Java代码   收藏代码
  1. <span style="font-size: small;">package threadConcurrent.test;  
  2.   
  3. import java.util.concurrent.CountDownLatch;  
  4.   
  5. /** 
  6.  * 分部执行任务 
  7.  *  
  8.  * @author liuyan 
  9.  *  
  10.  */  
  11. public class CountDownLatchDemo implements Runnable {  
  12.     private int id;  
  13.   
  14.     // 线程之间不影响,到了终点后继续做自己的事情  
  15.     private CountDownLatch countDownLatch;  
  16.   
  17.     public CountDownLatchDemo(int id, CountDownLatch countDownLatch) {  
  18.         this.id = id;  
  19.         this.countDownLatch = countDownLatch;  
  20.     }  
  21.   
  22.     @SuppressWarnings("static-access")  
  23.     @Override  
  24.     public void run() {  
  25.         try {  
  26.   
  27.             System.out.println("第" + (id + 1) + "小老婆开始查找《四十二章经》...");  
  28.             Thread.currentThread().sleep(id * 1000);  
  29.             System.out.println("第" + (id + 1) + "本《四十二章经》找到");  
  30.   
  31.             //计数器将等待数字-1  
  32.             countDownLatch.countDown();  
  33.   
  34.             System.out.println("第" + (id + 1) + "小老婆继续干其他事情");  
  35.         } catch (InterruptedException e) {  
  36.             e.printStackTrace();  
  37.         }  
  38.     }  
  39.   
  40.     public static void main(String[] args) {  
  41.         CountDownLatch countDownLatch = new CountDownLatch(8);  
  42.   
  43.         for (int i = 0; i < 8; i++) {  
  44.             new Thread(new CountDownLatchDemo(i, countDownLatch)).start();  
  45.         }  
  46.   
  47.         try {  
  48.   
  49.             System.out.println("韦小宝等着等着8本四十二章……");  
  50.   
  51.             // 韦小宝等着等着  
  52.             countDownLatch.await();  
  53.   
  54.             // 等待运动员到达终点  
  55.             System.out.println("8本四十二章经找寻完成,可以寻宝了!");  
  56.         } catch (InterruptedException e) {  
  57.             e.printStackTrace();  
  58.         }  
  59.     }  
  60. }</span>  

 

主线程就当做是韦小宝吧,主线程首先开辟了线程计数器对象,之后就开辟了8个线程,派出了8个小老婆去办事,之后主线程调用countDownLatch.await()阻塞,在家里一直喝着小茶,听着小曲等着8个线程的执行完毕。咱们来看小老婆们,小老婆们就辛苦了,开始找寻经书,之后调用countDownLatch.countDown()方法通知线程计数器减去1,让等待的线程减去1。就好比说有个小老婆找到了《四十二章经》,用iphone发个彩信将经书的夹缝地图发给韦小宝,韦小宝收到了,恩,这个老婆能干,任务完成,我的等待目标减去1。等到等待线程为0的时候,小宝开始行动了,将手机里的地图通过游戏——拼图游戏,一拼凑,大事可成,自己寻宝去!这里大家也看到了CountDownLatch与FutureTask的区别。CountDownLatch侧重的是分线程的完成个数,每次完成一个分线程,等待数目减少一个,等待线程为0的时候,主线程的就不阻塞了,开始往下走。而分线程一旦调用countDownLatch.countDown()方法,就代表分线程任务搞定,主线程就不会因为你的其他事情而不能往下走,完成任务了,小老婆们也可以去旅旅游,休息休息!而FutureTask则是注重执行结果的,主线程需要它的确切结果。所以futureTask执行的call()有返回值。

6.  CyclicBarrier

CyclicBarrier相对于CountDownLatch来说,最大的不同是,分线程具体的执行过程受其他分线程的影响,必须每个分线程都执行完毕了,主线程才继续往下走,而分线程如果在所有分线程执行完毕后还有其他动作,ok,还你自由,不必阻塞了,往下走你的路吧。这个例子是网上的游戏玩家的例子,4个小孩玩游戏,游戏要求必须是4个小孩都得通过第一关,才能开启第二关的关口!否则其他完成第一关的人都得等着其他人完成。这个有点像我们的项目开发,分模块开发,到一定阶段将模块汇总,联调,测试,如果这时候有一个模块没完成,大家等着吧,大家都在那里静静地、盯着你、等着你。

Java代码   收藏代码
  1. <span style="font-size: small;">package threadConcurrent.test;  
  2.   
  3. import java.util.concurrent.BrokenBarrierException;  
  4. import java.util.concurrent.CyclicBarrier;  
  5.   
  6. public class GameBarrier {  
  7.     public static void main(String[] args) {  
  8.         CyclicBarrier cyclicBarrier = new CyclicBarrier(4new Runnable() {  
  9.   
  10.             @Override  
  11.             public void run() {  
  12.                 System.out.println("所有玩家进入第二关!");  
  13.             }  
  14.         });  
  15.   
  16.         for (int i = 0; i < 4; i++) {  
  17.             new Thread(new Player(i, cyclicBarrier)).start();  
  18.         }  
  19.     }  
  20.   
  21. }  
  22.   
  23. class Player implements Runnable {  
  24.       
  25.     /** 
  26.      * 线程之间需要交互,到一定的条件下,所有线程才能往下走 
  27.      */  
  28.     private CyclicBarrier cyclicBarrier;  
  29.     private int id;  
  30.   
  31.     public Player(int id, CyclicBarrier cyclicBarrier) {  
  32.         this.cyclicBarrier = cyclicBarrier;  
  33.         this.id = id;  
  34.     }  
  35.   
  36.     @Override  
  37.     public void run() {  
  38.         try {  
  39.             System.out.println("玩家" + id + "正在玩第一关...");  
  40.             cyclicBarrier.await();  
  41.             System.out.println("玩家" + id + "进入第二关...");  
  42.         } catch (InterruptedException e) {  
  43.             e.printStackTrace();  
  44.         } catch (BrokenBarrierException e) {  
  45.             e.printStackTrace();  
  46.         }  
  47.     }  
  48. }</span>  

 使用cyclicBarrier.await();方法进行等待、阻塞,当所有分线程执行完毕了,主线程开始执行,分线程的自由也解脱了,继续往下走,开始第二关。

7.  Exchanger

Exchanger是线程资源交换器,线程A与线程B在某个运行阶段需要互换资源才能完成任务。这就好比2个公司职员——叶小钗和一页书。分别在不同的项目组——组A和组B,两个组开发者不同的项目,在正常时候叶小钗在组A上班开发者BOSS项目,一页书在项目组B开发ESB中间件产品。而在特殊时期项目组B不需要一页书了,需要叶小钗提供技术支持,就和项目组A要叶小钗,项目组A的leader也不是吃素的,你要叶小钗,没问题,把一页书也得接我们项目组剥削几天!就这样项目组B与项目组A做了这种“交易”(交换),用完了之后,恩~看程序吧

Java代码   收藏代码
  1. <span style="font-size: small;">package threadConcurrent.test;  
  2.   
  3. import java.util.concurrent.Exchanger;  
  4.   
  5. /** 
  6.  * 资源交换 
  7.  * @author liuyan 
  8.  */  
  9. public class ExgrDemo {  
  10.     public static void main(String args[]) {  
  11.           
  12.         //交换器  
  13.         Exchanger<String> exgr = new Exchanger<String>();  
  14.   
  15.         new TeamB(exgr);  
  16.         new TeamA(exgr);  
  17.     }  
  18. }  
  19.   
  20. /** 
  21.  * 项目组A 
  22.  * @author liuyan 
  23.  */  
  24. class TeamA implements Runnable {  
  25.     Exchanger<String> ex;  
  26.   
  27.     String str;  
  28.   
  29.     TeamA(Exchanger<String> c) {  
  30.         ex = c;  
  31.         str = new String();  
  32.   
  33.         new Thread(this).start();  
  34.     }  
  35.   
  36.     public void run() {  
  37.         char ch = 'A';  
  38.         for (int i = 0; i < 3; i++) {  
  39.             for (int j = 0; j < 5; j++)  
  40.                 str += (char) ch++;  
  41.   
  42.             try {  
  43.                 str = ex.exchange(str);  
  44.             } catch (InterruptedException exc) {  
  45.                 System.out.println(exc);  
  46.             }  
  47.         }  
  48.     }  
  49. }  
  50.   
  51. /** 
  52.  * 项目组B 
  53.  * @author liuyan 
  54.  */  
  55. class TeamB implements Runnable {  
  56.     Exchanger<String> ex;  
  57.   
  58.     String str;  
  59.   
  60.     TeamB(Exchanger<String> c) {  
  61.         ex = c;  
  62.         new Thread(this).start();  
  63.     }  
  64.   
  65.     public void run() {  
  66.   
  67.         for (int i = 0; i < 3; i++) {  
  68.             try {  
  69.                 str = ex.exchange(new String());  
  70.                 System.out.println("Got: " + str);  
  71.             } catch (InterruptedException exc) {  
  72.                 System.out.println(exc);  
  73.             }  
  74.         }  
  75.     }  
  76. }  
  77. </span>  

        结果为:

       Got : ABCDE

       Got : FGHIJ

       Got :KLMNO


需要说明的就是这种交换一定是成对儿的,就是交换的线程数目一定是偶数。否则奇数个线程,剩下那一个和谁交换去?这也是“等价交易”的一种体现。

8.  总结

这次主要介绍了并发环境下常常使用的并发包,用于控制多线程的并发调度、同步、交互、交换、协作等等。使用这些协作同步器,可以更灵活的处理线程之间的关系。也能更好地使用硬件资源为我们的并发系统提供高效率的运行能力。当然这次总结仅仅限于使用的层次,底层的实现源码分析有时间再做总结。

转发于:http://suhuanzheng7784877.iteye.com/blog/1145289

这篇关于Java分布式应用学习笔记05多线程下的并发同步器----后篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/664902

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis