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

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06