之所以把Latch与Barrier放在一起比较是因为他们给人一种相似的感觉。
他们都是阻塞一些行为直至某个事件发生,但Latch是等待某个事件发生,而Barrier是等待线程。

先比较一下JCIP中对二者的描述:

  • Latch

    A latch is a synchronizer that can delay the progress of threads until it reaches its terminal state.
    A latch acts as a gate: until the latch reaches the terminal state the gate is closed and no thread can pass, and in the terminal state the gate opens, allowing all threads to pass.
    Once the latch reaches the terminal state, it cannot change state again, so it remains open forever.
    Latches can be used to ensure that certain activities do not proceed until other one-time activities complete。

    即,闭锁可以延迟线程执行直至达到相应的结束状态。闭锁就像一个大门,未到达结束状态相当于大门紧闭,不让任何线程通过。
    而到达结束状态后,大门敞开,让所有的线程通过,但是一旦敞开后不会再关闭。
    闭锁可以用来确保一些活动在某个事件发生后执行。

  • Barrier

    CyclicBarrier allows a fixed number of parties to rendezvous repeatedly at a barrier point and is useful in parallel iterative algorithms that break down a problem into a fixed number of independent subproblems.
    Threads call await when they reach the barrier point, and await blocks until all the threads have reached the barrier point.
    If all threads meet at the barrier point, the barrier has been successfully passed, in which case all threads are released and the barrier is reset so it can be used again.

    很多人都把Barrier直译为"栅栏",我也很喜欢这个叫法。
    栅栏可以使一组执行在一处汇集,也就是说我们可以用栅栏将一个问题分解成多个独立的子问题,并在执行结束后在同一处进行汇集。
    当线程到达汇集地后调用await,await方法会出现阻塞直至其他线程也到达汇集地。
    如果所有的线程都到达就可以通过栅栏,也就是所有的线程得到释放,而且栅栏也可以被重新利用。

另外,下面javadoc中对二者之间区别的说明:

A CountDownLatch is initialized with a given count.
The await methods block until the current count reaches zero due to invocations of the countDown method, after which all waiting threads are released and any subsequent invocations of await return immediately.
This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

闭锁从来都是带着事件的触发次数。
await方法会一直阻塞至countDown方法将次数变成0为止,所有的线程被释放才能进行后续的工作。
但这种现象只能出现一次,也就是说触发次数不会被重置。
如果你想要一个可重置次数的闭锁,那就用栅栏。

Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor.
When all sub-parts are complete, the coordinating thread will be able to pass through await.
(When threads must repeatedly count down in this way, instead use a CyclicBarrier.)

这种行为阻塞的典型用法之一就是将某个问题分成多个部分,每个部分用不同的线程负责,并记得减少闭锁设置的次数。
当所有线程的工作结束后将通过await方法造成的阻塞,如果我们需要反复进行这样的工作就需要使用栅栏。

好了,既然Doug Lea老师将同一个观点阐述了这么多遍,剩下就是放心大胆地使用了,也许我们将问题想得太复杂了。
下面贴出栗子,由一个startGate拦住所有线程的执行,当所有线程就绪完成后调用countDown将它们释放,而另一扇大门——endGate后面正等着计算执行时间,而endGate等待的事件由这些线程触发:

public class TestHarness {
public static long timeTasks(int nThreads, final Runnable task)
throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads); for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
startGate.await();
try {
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored) {
}
}
};
t.start();
} long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}

执行看看:

public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("cost :::"+TestHarness.timeTasks(10,new Runnable() {
@Override
public void run() {
int num = RandomUtils.nextInt(0,100);
if(num>50) try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Alvez ::"+ num);
}
}));
}

接着试试栅栏,没有搞任何复杂的东西,注意countSupposed%partyCount,主要是想用这个体现一下栅栏是否可以反复使用多次。
如果countSupposed%partyCount的结果恰好为0,所有线程执行结束后,主线程也会正常结束。
反之则会一直阻塞下去,如果countSupposed%partyCount结果大于1且不为0,其结果就是我们看到"let's go to the barrier !!"出现的次数:

public static void barrierTest(int partyCount, int countSupposed) {

    if(partyCount<1 || countSupposed < 1) throw new IllegalArgumentException();

    final CyclicBarrier barrier = new CyclicBarrier(partyCount,new Runnable() {
@Override
public void run() {
System.out.println("let's go barrier !!");
}
});
System.out.println(countSupposed%partyCount==0?"let's show the smooth!!":"....but blocked"); for (int i = 0; i < countSupposed; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
barrier.await();
System.out.println(Thread.currentThread().getName() + " show!!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}
}

执行:

public static void main(String[] args) {
barrierTest(11, 20);
}

Java - Latch和Barrier的区别的更多相关文章

  1. java 接口和抽象类的区别

    java 接口和抽象类的区别抽象类:1.含有抽象方法的类一定为抽象类,反过来抽象类,不一定含有抽象方法:2.抽象类必须用abstract来进行定义,抽象方法也必须用abstract来进行定义:3.抽象 ...

  2. JDK与Java SE/EE/ME的区别

    1. Java SE(Java Platform,Standard Edition). Java SE 以前称为 J2SE.它允许开发和部署在桌面.服务器.嵌入式环境和实时环境中使用的 Java 应用 ...

  3. java 静态方法和实例方法的区别

    转自 java 静态方法和实例方法的区别 静态方法和实例方法的区别主要体现在两个方面:   在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法 ...

  4. java中ArrayList 、LinkList区别

    转自:http://blog.csdn.net/wuchuanpingstone/article/details/6678653 个人建议:以下这篇文章,是从例子说明的方式,解释ArrayList.L ...

  5. java抽象类与接口的区别及用法

    java抽象类与接口的区别及用法 一.抽象类里面的方法可以有实现,但是接口里面的方法确是只能声明. 二.接口是设计的结果 :抽象类是重构的结果 . 三.java不支持多重继承,所以继承抽象类只能继承一 ...

  6. 【转】java int与integer的区别

    java int与integer的区别 int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1 ...

  7. java 中 ==和equals 的区别

      Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolea ...

  8. java中equals和==的区别 (转)

    java中equals和==的区别  值类型是存储在内存中的堆栈(以后简称栈),而引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中. ==操作比较的是两个变量的值是否相等,对于引 ...

  9. 【转】Java中equals和==的区别

    [转]Java中equals和==的区别 java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boole ...

随机推荐

  1. Struts2学习第4天--拦截器

    第1章     Struts2_day04笔记 1.1      上次课内容回顾 l  OGNL表达式 n  OGNL的概述 u  OGNL:对象图导航语言,是一门功能强大的表达式语言. n  OGN ...

  2. 转载-----Java Longest Palindromic Substring(最长回文字符串)

    转载地址:https://www.cnblogs.com/clnchanpin/p/6880322.html 假设一个字符串从左向右写和从右向左写是一样的,这种字符串就叫做palindromic st ...

  3. Python操作配置文件configparser模块

    在实际的开发过程中,我们常有操作ini格式和conf格式配置文件的操作,Python为我们提供了configparser模块,方便我们对配置文件进行读写操作. config.ini配置文件内容如下: ...

  4. AutoCAD.Net圆弧半径标注延长线

    #region 注册RegApp public static void CheckRegApp(string regapptablename) { Database db = HostApplicat ...

  5. PHP程序执行流程

    1, PHP文件一定放在服务器的,但是PHP中不同的内容会在不同的地方执行.下图演示了浏览器请求一个php页面的流程. 2,时序图如下所示,在浏览器中输入url后,首先去本机hosts文件中解析ip地 ...

  6. Linux Shell 自动化之让文本飞

    Linux Shell 自动化之让文本飞 一.前言: ​ 作者之前在一家 IDC 从事运维兼职工作,后来因某些原因辞职开始 Python 爬虫数据分析.因为这些经历以及后续时间积累下的经验,发现好像自 ...

  7. Squid代理服务器(三)——ACL访问控制

    一.ACL概念 Squid提供了强大的代理控制机制,通过合理设置ACL(Access Control List,访问控制列表)并进行限制,可以针对源地址.目标地址.访问的URL路径.访问的时间等各种条 ...

  8. easyui里面的API=====》 load

    在easyui里面有个API load,这个API实用性质很强,可以直接帮我加载页面的数据到想需要的相应输入框里面,但是也要注意,这些将要被填入数据的输入框里面的属性有个要求:其中输入框里面的name ...

  9. java多线程那点事

    屌丝程序员们对自己的技术能力总是毫不掩饰的高调,更有甚者每当完成一个简单的功能或算法实现,恨不得从工位上跳起来,生怕谁不知道一样,心情能理解,但个人完全鄙视这种行为.说到底,大家日常的coding,大 ...

  10. js数组的常用操作

    数组合并 var arr=[1,"abc","张三","122"]; var b=["今天天气不错","适合学 ...