CountDownLatch

CountDownLatch用来使一个线程或多个线程等待到其他线程完成。CountDownLatch有个初始值count,await方法会阻塞线程,直到通过countDown方法调用使count减少为0才会执行await方法后面的代码。

示例代码

MyThread50_0是WorkThread,不同的线程休眠时间不一样。

public class MyThread50_0 extends Thread
{
private CountDownLatch cdl;
private int sleepSecond; public MyThread50_0(String name, CountDownLatch cdl, int sleepSecond)
{
super(name);
this.cdl = cdl;
this.sleepSecond = sleepSecond;
} public void run()
{
try
{
System.out.println(this.getName() + "启动了,时间为" + new Date());
Thread.sleep(sleepSecond * 1000);
cdl.countDown();
System.out.println(this.getName() + "执行完了,时间为" + new Date());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

MyThread50_1是DoneThread和main方法

public class MyThread50_1 extends Thread {
private CountDownLatch cdl; public MyThread50_1(String name, CountDownLatch cdl)
{
super(name);
this.cdl = cdl;
} public void run()
{
try
{
System.out.println(this.getName() + "要等待了, 时间为" + new Date());
cdl.await();
System.out.println(this.getName() + "等待完了, 时间为" + new Date());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(3);
MyThread50_1 dt0 = new MyThread50_1("DoneThread1", cdl);
MyThread50_1 dt1 = new MyThread50_1("DoneThread2", cdl);
dt0.start();
dt1.start();
MyThread50_0 wt0 = new MyThread50_0("WorkThread1", cdl, 2);
MyThread50_0 wt1 = new MyThread50_0("WorkThread2", cdl, 3);
MyThread50_0 wt2 = new MyThread50_0("WorkThread3", cdl, 4);
wt0.start();
wt1.start();
wt2.start();
}
}

运行结果如下

DoneThread2要等待了, 时间为Sun Sep 22 21:37:57 CEST 2019
DoneThread1要等待了, 时间为Sun Sep 22 21:37:57 CEST 2019
WorkThread3启动了,时间为Sun Sep 22 21:37:57 CEST 2019
WorkThread2启动了,时间为Sun Sep 22 21:37:57 CEST 2019
WorkThread1启动了,时间为Sun Sep 22 21:37:57 CEST 2019
WorkThread1执行完了,时间为Sun Sep 22 21:37:59 CEST 2019
WorkThread2执行完了,时间为Sun Sep 22 21:38:00 CEST 2019
WorkThread3执行完了,时间为Sun Sep 22 21:38:01 CEST 2019
DoneThread2等待完了, 时间为Sun Sep 22 21:38:01 CEST 2019
DoneThread1等待完了, 时间为Sun Sep 22 21:38:01 CEST 2019

“DoneThreadX要等待了”和“WorkThreadX启动了”的顺序是随机的。

“WorkThreadX执行完了“的顺序按照1、2、3,因为我们的等待时间2、3、4秒。

待WorkThread3执行完了,才会执行await()之后的代码,DoneThreadX执行完了,同样该顺序随机。

这是一种加强版的等待/通知模型,它可以实现多个工作线程完成任务后通知多个等待线程开始工作。

我们之前的等待/通知模型只能实现一个工作线程完成任务后通知一个等待线程或者所有等待线程开始工作。

Semaphore

Semaphore用来控制并发数量,Semaphore构造函数传入permit(许可),一个permit相当于一个不可重入锁,acquire方法获得permit,relase方法归还permit。

代码示例如下

public class MyThread51 {
public static void main(String[] args)
{
final Semaphore semaphore = new Semaphore(5); Runnable runnable = new Runnable()
{
public void run()
{
try
{
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "获得了permit,时间为" + new Date());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "释放了permit,时间为" + new Date()); }
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
semaphore.release();
}
}
}; Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++)
threads[i] = new Thread(runnable);
for (int i = 0; i < threads.length; i++)
threads[i].start();
}
}

运行结果如下

Thread-2获得了permit,时间为Sun Sep 29 21:47:05 CEST 2019
Thread-3获得了permit,时间为Sun Sep 29 21:47:05 CEST 2019
Thread-4获得了permit,时间为Sun Sep 29 21:47:05 CEST 2019
Thread-1获得了permit,时间为Sun Sep 29 21:47:05 CEST 2019
Thread-0获得了permit,时间为Sun Sep 29 21:47:05 CEST 2019
Thread-3释放了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-1释放了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-0释放了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-2释放了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-4释放了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-5获得了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-7获得了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-6获得了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-9获得了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-8获得了permit,时间为Sun Sep 29 21:47:07 CEST 2019
Thread-5释放了permit,时间为Sun Sep 29 21:47:09 CEST 2019
Thread-8释放了permit,时间为Sun Sep 29 21:47:09 CEST 2019
Thread-9释放了permit,时间为Sun Sep 29 21:47:09 CEST 2019
Thread-6释放了permit,时间为Sun Sep 29 21:47:09 CEST 2019
Thread-7释放了permit,时间为Sun Sep 29 21:47:09 CEST 2019

2,3,4,1,0先获得了permit,相差两秒释放了permit;

5,7,6,9,8获得了permit,相差两秒释放了permit;

因为我们设置的permit是5,所有只能有五个线程获得permit。

Exchanger

Exchanger用来交换两个线程中的数据

示例代码如下

public class MyThread52 extends Thread{
private String str;
private Exchanger<String> exchanger;
private int sleepSecond; public MyThread52(String str, Exchanger<String> exchanger, int sleepSecond) {
this.str = str;
this.exchanger = exchanger;
this.sleepSecond = sleepSecond;
} public void run() {
try {
System.out.println(this.getName() + "启动, 原数据为" + str + ", 时间为" + new Date());
Thread.sleep(sleepSecond * 1000);
str = exchanger.exchange(str);
System.out.println(this.getName() + "交换了数据, 交换后的数据为" + str + ", 时间为" + new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<String>();
MyThread52 et0 = new MyThread52("111", exchanger, 3);
MyThread52 et1 = new MyThread52("222", exchanger, 2); et0.start();
et1.start();
}
}

运行结果如下

Thread-1启动, 原数据为222, 时间为Sun Sep 29 22:18:36 CEST 2019
Thread-0启动, 原数据为111, 时间为Sun Sep 29 22:18:36 CEST 2019
Thread-0交换了数据, 交换后的数据为222, 时间为Sun Sep 29 22:18:39 CEST 2019
Thread-1交换了数据, 交换后的数据为111, 时间为Sun Sep 29 22:18:39 CEST 2019

可以看到,数据发生了交换,时间差为最长时间3s。

CyclicBarrier

一组线程等待对方都达到barrier point,再执行接下来的动作,barrier point是循环的,它可以重用。

示例代码如下

public class MyThread53 extends Thread{
private CyclicBarrier cb;
private int sleepSecond; public MyThread53(CyclicBarrier cb, int sleepSecond)
{
this.cb = cb;
this.sleepSecond = sleepSecond;
} public void run()
{
try
{
System.out.println(this.getName() + "运行了");
System.out.println(this.getName() + "准备等待了, 时间为" + new Date());
Thread.sleep(sleepSecond * 1000); cb.await();
System.out.println(this.getName() + "结束等待了, 时间为" + new Date());
}
catch (Exception e)
{
e.printStackTrace();
}
} public static void main(String[] args)
{
Runnable runnable = new Runnable()
{
public void run()
{
System.out.println("CyclicBarrier的所有线程await()结束了,我运行了, 时间为" + new Date());
}
};
//需要等待三个线程await()后再执行runnable
CyclicBarrier cb = new CyclicBarrier(3, runnable);
MyThread53 cbt0 = new MyThread53(cb, 3);
MyThread53 cbt1 = new MyThread53(cb, 6);
MyThread53 cbt2 = new MyThread53(cb, 9);
cbt0.start();
cbt1.start();
cbt2.start();
}
}

运行结果如下

Thread-0运行了
Thread-1运行了
Thread-2运行了
Thread-1准备等待了, 时间为Mon Sep 30 23:02:11 CEST 2019
Thread-2准备等待了, 时间为Mon Sep 30 23:02:11 CEST 2019
Thread-0准备等待了, 时间为Mon Sep 30 23:02:11 CEST 2019
CyclicBarrier的所有线程await()结束了,我运行了, 时间为Mon Sep 30 23:02:20 CEST 2019
Thread-2结束等待了, 时间为Mon Sep 30 23:02:20 CEST 2019
Thread-0结束等待了, 时间为Mon Sep 30 23:02:20 CEST 2019
Thread-1结束等待了, 时间为Mon Sep 30 23:02:20 CEST 2019

Runnable线程在Thread-0,Thread-1,Thread-2 await()后运行,Runnable线程和三个线程的执行时间几乎相同。

Callable和Future

Callable

由于Runnable接口run()返回值是void类型,执行任务后无法返回结果。所以我们需要Callable接口,该接口的call()可以返回值。

Future

Future表示一个异步计算结果,Future提供了如下方法

get():获取任务执行结果

cancel():中断任务

isDone():判断任务是否执行完成

isCancelled():判断任务是否被取消

示例代码如下

public class MyThread54 implements Callable<String> {
public String call() throws Exception
{
System.out.println("进入CallableThread的call()方法, 开始睡觉, 睡觉时间为" + new Date());
Thread.sleep(10000);
return "是ss12";
} public static void main(String[] args) throws Exception
{
ExecutorService es = Executors.newCachedThreadPool();
MyThread54 ct = new MyThread54();
Future<String> f = es.submit(ct);
es.shutdown(); Thread.sleep(5000);
System.out.println("主线程等待5秒, 当前时间为" + new Date()); String str = f.get();
System.out.println("Future已拿到数据, str = " + str + ", 当前时间为" + new Date());
}
}

运行结果如下

进入CallableThread的call()方法, 开始睡觉, 睡觉时间为Sun Nov 03 11:00:22 CET 2019
主线程等待5秒, 当前时间为Sun Nov 03 11:00:27 CET 2019
Future已拿到数据, str = 是ss12, 当前时间为Sun Nov 03 11:00:32 CET 2019

可以看到,Future在10s后拿到了返回结果。

Java多线程(十五):CountDownLatch,Semaphore,Exchanger,CyclicBarrier,Callable和Future的更多相关文章

  1. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  2. java多线程系列(八)---CountDownLatch和CyclicBarrie

    CountDownLatch 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线 ...

  3. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  4. Java多线程(五)线程的生命周期

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  5. “全栈2019”Java多线程第五章:线程睡眠sleep()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java第九十五章:方法中可以定义静态局部内部类吗?

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  7. “全栈2019”Java第十五章:Unicode与转义字符

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. “全栈2019”Java第二十五章:流程控制语句中循环语句while

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  9. Java程序员必须掌握的线程知识-Callable和Future

    Callable和Future出现的原因 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需 ...

随机推荐

  1. opencv边缘检测

    人眼怎么识别图像边缘? 比如有一幅图,图里面有一条线,左边很亮,右边很暗,那人眼就很容易识别这条线作为边缘.也就是像素的灰度值快速变化的地方. sobel算子 sobel算子是一个离散差分算子. 图像 ...

  2. 23种设计模式之模板方法(Template Pattern)

    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤 AbstractClass:抽象类.用来定义算法骨架和原语操作,在这个类里 ...

  3. Spring 梳理-webApplicationContext 与servletContext

    1.WebApplicationContext的研究 ApplicationContext是spring的核心,Context通常解释为上下文环境,用“容器”来表述更容易理解一些,Applicatio ...

  4. SharePoint 2013 Create Folder with conententtype programer

    记录一下昨天写的SharePoint tool,需求是这样的: 在SharePoint list subfolder 下创建1000个folder,这些folder指定特殊的contenttype,c ...

  5. JS 取整、取余

    一.取整 1. 取整 // 丢弃小数部分,保留整数部分 parseInt(7/2) // 3 2. 向上取整 // 向上取整,有小数就整数部分加1 Math.ceil(7/2) // 4 3. 向下取 ...

  6. python爬虫—— 抓取今日头条的街拍的妹子图

    AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新. 近期在学习获取j ...

  7. Java后台开发方向面试题集合

    内容会不断更新. 初衷是每次看面经肯定都会有一些一时反应不过来的问题,希望集中记录一下便于自己查看. 而答案部分谷歌就很好,当然有些问题可能需要多次谷歌. 对于一些记不住的答案,我也会持续写上一些. ...

  8. python爬虫——爬取B站用户在线人数

    国庆期间想要统计一下bilibili网站的在线人数变化,写了一个简单的爬虫程序.主要是对https://api.bilibili.com/x/web-interface/online返回的参数进行分析 ...

  9. [Android Studio] 2019年Android Studio配置指北

    Android Studio是我学习Android开发路上的第一块绊脚石,新建一个项目,一行代码没动,直接编译不起来,我太难了,所以本文叫指北 本文讲解在9102年如何在国内网络不通畅的情况下流畅的使 ...

  10. (八十四)c#Winform自定义控件-导航菜单(类Office菜单)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...