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. mybatis简单入门介绍

    mybatis入门 简介 什么是mybatis? MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及 ...

  2. 【Elasticsearch 搜索之路】(一)什么是 Elasticsearch?

    本篇文章对 Elasticsearch 做了基本介绍,在后续将通过专栏的方式持续更新,本系列以 Elasticsearch7 作为主要的讲解版本,欢迎各位大佬指正,共同学习进步! 一般涉及大型数据库的 ...

  3. Tomcat线程参数maxThreads、acceptCount

    一.配置Tomcat/conf/server.xml修改配置 <Connector port="8080" protocol="org.apache.coyote. ...

  4. XCTF-CAT

    果然还是我太菜了呜呜呜,这道题仍然是没有自己做出来.哎. 这一道用的并不是PHP的环境,而是用Python中的Django编写的. 记得做过类似的一道题目.来源于MOCTF中的网站扫描器,当时做完后其 ...

  5. 04-02 AdaBoost算法

    目录 AdaBoost算法 一.AdaBoost算法学习目标 二.AdaBoost算法详解 2.1 Boosting算法回顾 2.2 AdaBoost算法 2.3 AdaBoost算法目标函数优化 三 ...

  6. 记录一次jvm内存泄露的问题

    前些天,运维告诉我刚上线的java服务占用CPU过高. 以下是发现解决问题的具体流程. 1:通过#top命令查看,我的java服务确实把CPU几乎占满了,如图 可看到18400这个进程CPU占用达到了 ...

  7. java猜数游戏

    java随机数的产生 int number=(int)(Math.random()*10+1) Math.random()*n //n个随机数,从0开始 do{}while循环 //猜数,1到10的随 ...

  8. SVN部署(基于Linux)

    第一步:通过yum命令安装svnserve,命令如下: yum -y install subversion 此命令会全自动安装svn服务器相关服务和依赖,安装完成会自动停止命令运行 若需查看svn安装 ...

  9. Python_函数传参

    关于函数中传递参数的相关知识 其中 万能参数 第一次听说 但感觉用处不大 后面用到再详细整理

  10. 代码审计-EasyCMS

    cms来源AWD线下攻防平台题目.  链接:https://pan.baidu.com/s/1eUkyRspQmsv-0fIBby8ZlQ  提取码:tywa 失效可以联系我   0x01 文件上传漏 ...