-闭锁(Latch)

闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。

应用场景:

  • 确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示“资源R已经被初始化”,而所有需要R的操作都必须先在这个闭锁上等待。
  • 确保某个服务在其依赖的所有其他服务都已经启动之后才启动。
  • 等待直到某个操作的所有参与者都就绪在继续执行。(例如:多人游戏中需要所有玩家准备才能开始)
CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。CountDownLatch有一个正数计数器,countDown方法对计数器做减操作,await方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。


-栅栏(CyclicBarrier)

栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。

场景: 应用一些协议,比如几个家庭成员决定在某个地方集合,所有人在6:00在某地集合,到了以后要等待其他人,之后才能讨论去哪里吃饭。 并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await()),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。


-例子:两个分别关于CountDownlatch和CyclicBarrier的例子

1、CountDownLatch

有三个工人在为老板干活,这个老板有一个习惯,就是当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。记住这个条件:三个工人先全部干完活,老板才检查。所以在这里用Java代码设计两个类,Worker代表工人,Boss代表老板,具体的代码实现如下:

工人:

package LatchAndCyclicBarrier;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; public class Work implements Runnable{ private CountDownLatch downLatch;
private String name; public Work(CountDownLatch downLatch, String name){
this.downLatch = downLatch;
this.name = name;
} public void run() {
this.doWork();
try{
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
}catch(InterruptedException ie){
}
System.out.println(this.name + "活干完了!");
this.downLatch.countDown(); } private void doWork(){
System.out.println(this.name + "正在干活!");
} }

老板:


package LatchAndCyclicBarrier; import java.util.concurrent.CountDownLatch; public class Boss implements Runnable{ private CountDownLatch downLatch; public Boss(CountDownLatch downLatch){
this.downLatch = downLatch;
} public void run() {
System.out.println("老板正在等所有的工人干完活......");
try {
this.downLatch.await();
} catch (InterruptedException e) {
}
System.out.println("工人活都干完了,老板开始检查了!");
} }

测试代码:

package LatchAndCyclicBarrier;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class TestLatch { public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool(); CountDownLatch latch = new CountDownLatch(3); Work w1 = new Work(latch,"张三");
Work w2 = new Work(latch,"李四");
Work w3 = new Work(latch,"王二"); Boss boss = new Boss(latch); executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss); executor.shutdown();
} }

执行结果:

李四正在干活!
老板正在等所有的工人干完活......
王二正在干活!
张三正在干活!
李四活干完了!
王二活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!

2、CyclicBarrier

接着上面的例子,还是这三个工人,不过这一次,这三个工人自由了,老板不用检查他们任务了,他们三个合作建桥,有三个桩,每人打一个,同时打完之后才能一起搭桥(搭桥需要三人一起合作)。也就是说三个人都打完桩之后才能继续工作。


package LatchAndCyclicBarrier; import java.util.concurrent.CyclicBarrier; public class CycWork implements Runnable { private CyclicBarrier cyclicBarrier ;
private String name ; public CycWork(CyclicBarrier cyclicBarrier,String name)
{
this .name =name;
this .cyclicBarrier =cyclicBarrier;
} @Override
public void run() {
// TODO Auto-generated method stub System. out .println(name +"正在打桩,毕竟不轻松。。。。。" ); try {
Thread. sleep(5000);
System. out .println(name +"不容易,终于把桩打完了。。。。" );
cyclicBarrier .await(); } catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
} System. out .println(name +":其他逗b把桩都打完了,又得忙活了。。。" ); } }

测试程序

package LatchAndCyclicBarrier;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CycTest { public static void main(String[] args)
{
ExecutorService executorpool=Executors. newFixedThreadPool(3);
CyclicBarrier cyclicBarrier= new CyclicBarrier(3); CycWork work1= new CycWork(cyclicBarrier, "张三" );
CycWork work2= new CycWork(cyclicBarrier, "李四" );
CycWork work3= new CycWork(cyclicBarrier, "王五" ); executorpool.execute(work1);
executorpool.execute(work2);
executorpool.execute(work3); executorpool.shutdown(); } }

运行结果:

李四正在打桩,毕竟不轻松。。。。。
张三正在打桩,毕竟不轻松。。。。。
王五正在打桩,毕竟不轻松。。。。。
李四不容易,终于把桩打完了。。。。
张三不容易,终于把桩打完了。。。。
王五不容易,终于把桩打完了。。。。
王五:其他逗b把桩都打完了,又得忙活了。。。
李四:其他逗b把桩都打完了,又得忙活了。。。
张三:其他逗b把桩都打完了,又得忙活了。。。

CountDownlatch和CyclicBarrierde 源码部分

1、CountDownLatch中的两个关键方法

  public void countDown() {    //对计数器减一 表示有一个事件已经发生了
sync.releaseShared(1);
}
 public void await() throws InterruptedException { //等到计数器为0
sync.acquireSharedInterruptibly(1);
}
await方法调用了AbstractQueuedSynchronizer中的acquireSharedInterruptibly

 public final void acquireSharedInterruptibly (int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
 public final boolean releaseShared (int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true ;
}
return false ;
}
protected boolean tryReleaseShared (int arg) {
throw new UnsupportedOperationException();
}

2、CyclicBarrier中的await()方法

  public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen;
}
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation; if (g.broken)
throw new BrokenBarrierException(); if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
} int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
} // loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
} if (g.broken)
throw new BrokenBarrierException(); if (g != generation)
return index; if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
上面dowait方法中有一个index,index=--count而count的值在源码中来自

count = parties;

提到 parties就不得不看看构造函数了

 public CyclicBarrier(int parties) {
this(parties, null);
}

如上例子,我们构造了CyclicBarrier(3)那么此时的 count值为3,接着dowait源码,当index==0时,后面执行的

final Runnable command = barrierCommand;

其实是可以设置的,这个Runnable可以传进来,当我们希望所有线程都达到某一时刻之后,用什么线程执行接下来的工作,当没有传Runnable进来时,就继续执行(唤醒其他线程),否则就runnable.run()(唤醒其他线程之前执行)

java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)的更多相关文章

  1. Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)

    JAVA并发包中有三个类用于同步一批线程的行为,分别是闭锁(Latch),信号灯(Semaphore)和栅栏(CyclicBarrier).本贴主要说明闭锁(Latch)和栅栏(CyclicBarri ...

  2. 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  3. java 多线程并发系列之 生产者消费者模式的两种实现

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...

  4. java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

    这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...

  5. 从火箭发场景来学习Java多线程并发闭锁对象

    从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...

  6. Java高并发系列——检视阅读

    Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...

  7. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  8. Java多线程并发07——锁在Java中的实现

    上一篇文章中,我们已经介绍过了各种锁,让各位对锁有了一定的了解.接下来将为各位介绍锁在Java中的实现.关注我的公众号「Java面典」了解更多 Java 相关知识点. 在 Java 中主要通过使用sy ...

  9. [转]Java多线程干货系列—(一)Java多线程基础

    Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...

随机推荐

  1. SQLServer过期的解决方案

    看图吧,不喜欢说话,图里面我都打备注了 0SQLService异常 1找到安装中心 2升级版本 3监测ing 4输入升级key 5同意并下一步 6下一步 7下一步 8下一步 9收工 10可以打开了

  2. 手动生成moc文件

    在VS中写Qt项目时,手动添加了一个类,由于要用到信号槽,所以需要生成相应的moc文件.写好信号槽以后,在类里面第一行应该写上Q_OBJECT关键字,编译项目会提示无法找到moc_XXX.cpp文件. ...

  3. python---django中form组件(2)自定制属性以及表单的各种验证,以及数据源的实时更新,以及和数据库关联使用ModelForm和元类

    自定义属性以及各种验证 分析widget: class TestForm(forms.Form): user = fields.CharField( required = True, widget = ...

  4. 深入了解C指针

      前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级 ...

  5. Angular2新人常犯的5个错误

    看到这儿,我猜你肯定已经看过一些博客.技术大会录像了,现在应该已经准备好踏上angular2这条不归路了吧!那么上路后,哪些东西是我们需要知道的? 下面就是一些新手常见错误汇总,当你要开始自己的ang ...

  6. 关于Web安全的那些事(XSS攻击)

    概述 XSS攻击是Web攻击中最常见的攻击方法之一,它是通过对网页注入可执行代码且成功地被浏览器执行,达到攻击的目的,形成了一次有效XSS攻击,一旦攻击成功,它可以获取用户的联系人列表,然后向联系人发 ...

  7. HDU 2544 最短路 最短路问题

    解题报告: 这题就是求两个单源点之间的最小距离,属于最短路问题,由于数据量很小,只有100,所以这题可以用弗洛伊德也可以用迪杰斯特拉,都可以过,但是用迪杰斯特拉会快一点,但用弗洛伊德的代码会稍短一点, ...

  8. 第9月第15天 设计模式 adapter mvc

    1. 有一道iOS面试题,iOS中都有什么设计模式?很少有答案说包括adapter. gof 书中adapter模式有以下内容: 实现: ... b ) 使 用 代 理 对 象 在这种方法中, T r ...

  9. Anaconda+django写出第一个web app(十)

    今天继续学习外键的使用. 当我们有了category.series和很多tutorials时,我们查看某个tutorial,可能需要这样的路径http://127.0.0.1:8000/categor ...

  10. fuzz for test of the Net::HTTP::GET

    use Net::HTTP::GET; % %0e%0f ' *%26 @.jpg>; my $count = 0; for @chars X @chars X @chars X @chars ...