Java并发包——线程通信
Java并发包——线程通信
摘要:本文主要学习了Java并发包里有关线程通信的一些知识。
部分内容来自以下博客:
https://www.cnblogs.com/skywang12345/p/3496716.html
线程通信方式
对于线程之间的通信方式,我们之前使用Object.wait()和Object.notify(),通过与synchronized关键字进行同步,两者配合使用,可以实现线程之间的通信。
后来在JUC并发包里发现可以使用Lock取代synchronized关键字实现线程之间的同步,并且使用Lock的方式有比synchronized方式更加强大的功能,为了同Lock配合,实现线程之间的通信,就要用到Condition。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait()、notify()、notifyAll()方法是和“同步锁”(synchronized关键字)捆绑使用的,而Condition是需要与“独享锁/共享锁”捆绑使用的。
使用Condition的优势
能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
能够在多个线程之间进行通信。Object的wait()、notify()、notifyAll()方法只能实现两个线程之间的通信,而Lock对象能通过newCondition()方法创建出无数的“条件”,通过这些条件,我们就能够成功地实现多线程之间的数据通信,对它们进行控制。
Condition
Condition是java.util.concurrent.locks包下的一个接口,提供了用来进行线程通信的方法。
public interface Condition {
// 使当前线程加入等待队列中并释放当锁,被通知、被中断时唤醒。
void await() throws InterruptedException; // 同await()类似,只是该方法对中断不敏感,只有被通知时才被唤醒。
void awaitUninterruptibly(); // 同await()类似,如果在指定时间之内没有被通知或者被中断,该方法会返回false。
boolean await(long time, TimeUnit unit) throws InterruptedException; // 当前线程进入等待状态,被通知、中断或者超时之后被唤醒。返回值就是表示剩余的时间,超时返回值是0或者负数。
long awaitNanos(long nanosTimeout) throws InterruptedException; // 同awaitNanos(long nanosTimeout)类似,只是参数变成了指定日期。
boolean awaitUntil(Date deadline) throws InterruptedException; // 唤醒一个在等待队列中的线程。
void signal(); // 唤醒所有在等待队列中的线程。
void signalAll();
}
获取Lock上的特定Condition
Condition实例实质上被绑定到一个锁上。一个锁内部可以有多个Condition,即有多路等待和通知。要为特定Lock实例获得Condition实例,请使用Lock的newCondition()方法。
newCondition()返回用来与当前Lock实例一起使用的Condition实例。
类似于Object.wait()和Object.notify()的功能,Object.wait()与Object.notify()需要结合synchronized使用。Condition需要结合ReentrantLock使用。
使用Condition实现线程通信
使用synchronized和Object类的方法
使用synchronized和Object类的方法实现两个线程交替打印,代码如下:
public class Demo {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
Thread a = new Thread(demoThread, "线程A");
Thread b = new Thread(demoThread, "线程B");
a.start();
b.start();
}
} class DemoThread implements Runnable {
private Integer num = 1; @Override
public void run() {
synchronized (DemoThread.class) {
while (num <= 10) {
DemoThread.class.notify();
System.out.println(Thread.currentThread().getName() + " >>> " + num++);
if (num <= 10) {
try {
DemoThread.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
运行结果如下:
线程A >>> 1
线程B >>> 2
线程A >>> 3
线程B >>> 4
线程A >>> 5
线程B >>> 6
线程A >>> 7
线程B >>> 8
线程A >>> 9
线程B >>> 10
使用Lock和Condition类的方法
使用Lock和Condition类的方法实现两个线程交替打印,代码如下:
public class Demo {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
Thread a = new Thread(demoThread, "线程A");
Thread b = new Thread(demoThread, "线程B");
a.start();
b.start();
}
} class DemoThread implements Runnable {
private Integer num = 1;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition(); @Override
public void run() {
lock.lock();
try {
while (num <= 10) {
condition.signal();
System.out.println(Thread.currentThread().getName() + " >>> " + num++);
if (num <= 10) {
condition.await();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
运行结果如下:
线程A >>> 1
线程B >>> 2
线程A >>> 3
线程B >>> 4
线程A >>> 5
线程B >>> 6
线程A >>> 7
线程B >>> 8
线程A >>> 9
线程B >>> 10
使用Lock和Condition类的方法实现三个线程按顺序循环打印
使用Lock和Condition类的方法实现三个线程按顺序打印,需要唤醒指定的线程,代码如下:
public class Demo {
public static void main(String[] args) {
DemoThread demoThread = new DemoThread();
Thread a = new Thread(() -> demoThread.run1(), "线程1");
Thread b = new Thread(() -> demoThread.run2(), "线程2");
Thread c = new Thread(() -> demoThread.run3(), "线程3");
a.start();
b.start();
c.start();
}
} class DemoThread {
static private Integer num = 0;
static Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition(); public void run1() {
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
while (num < 10) {
for (int i = 0; i < 1 && num < 10; i++) {
System.out.println(Thread.currentThread().getName() + " >>> " + num);
}
num++;
c2.signal();
c1.await();
if (num >= 9) {
c3.signal();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void run2() {
try {
Thread.sleep(20);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
while (num < 10) {
for (int i = 0; i < 2 && num < 10; i++) {
System.out.println(Thread.currentThread().getName() + " >>> " + num);
}
num++;
c3.signal();
c2.await();
if (num >= 9) {
c1.signal();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void run3() {
try {
Thread.sleep(30);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
lock.lock();
try {
while (num < 10) {
for (int i = 0; i < 3 && num < 10; i++) {
System.out.println(Thread.currentThread().getName() + " >>> " + num);
}
num++;
c1.signal();
c3.await();
if (num >= 9) {
c2.signal();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
运行结果如下:
线程1 >>> 0
线程2 >>> 1
线程2 >>> 1
线程3 >>> 2
线程3 >>> 2
线程3 >>> 2
线程1 >>> 3
线程2 >>> 4
线程2 >>> 4
线程3 >>> 5
线程3 >>> 5
线程3 >>> 5
线程1 >>> 6
线程2 >>> 7
线程2 >>> 7
线程3 >>> 8
线程3 >>> 8
线程3 >>> 8
线程1 >>> 9
Condition中的await()、signal()、signalAll()与Object中的wait()、notify()、notifyAll()区别
使用方式不同
Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。
不同的是,Object中的这些方法是和同步锁捆绑使用的,而Condition是需要与互斥锁/共享锁捆绑使用的。
功能更加强大
Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒“读线程”。当从缓冲区读出数据之后,唤醒“写线程”。当缓冲区满的时候,“写线程”需要等待。当缓冲区为空时,“读线程”需要等待。
如果采用Object类中的wait()、notify()、notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒“读线程”时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。
但是,通过Condition,就能明确的指定唤醒读线程。
Java并发包——线程通信的更多相关文章
- java并发包&线程池原理分析&锁的深度化
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...
- Java并发基础--线程通信
java中实现线程通信的四种方式 1.synchronized同步 多个线程之间可以借助synchronized关键字来进行间接通信,本质上是通过共享对象进行通信.如下: public class S ...
- Java并发包——线程同步和锁
Java并发包——线程同步和锁 摘要:本文主要学习了Java并发包里有关线程同步的类和锁的一些相关概念. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520 ...
- Java并发包——线程安全的Collection相关类
Java并发包——线程安全的Collection相关类 摘要:本文主要学习了Java并发包下线程安全的Collection相关的类. 部分内容来自以下博客: https://www.cnblogs.c ...
- Java并发包——线程池
Java并发包——线程池 摘要:本文主要学习了Java并发包中的线程池. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520/p/3932921.html ...
- Java并发包——线程安全的Map相关类
Java并发包——线程安全的Map相关类 摘要:本文主要学习了Java并发包下线程安全的Map相关的类. 部分内容来自以下博客: https://blog.csdn.net/bill_xiang_/a ...
- Java并发包线程池之ForkJoinPool即ForkJoin框架(一)
前言 这是Java并发包提供的最后一个线程池实现,也是最复杂的一个线程池.针对这一部分的代码太复杂,由于目前理解有限,只做简单介绍.通常大家说的Fork/Join框架其实就是指由ForkJoinPoo ...
- java笔记--关于线程通信
关于线程通信 使用多线程编程的一个重要原因就是线程间通信的代价比较小 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897773.h ...
- Java并发包线程池之Executors、ExecutorCompletionService工具类
前言 前面介绍了Java并发包提供的三种线程池,它们用处各不相同,接下来介绍一些工具类,对这三种线程池的使用. Executors Executors是JDK1.5就开始存在是一个线程池工具类,它定义 ...
随机推荐
- 学习笔记 第十三章 使用CSS3新布局
第13章 使用CSS3新布局 [学习重点] 设计多列布局 设计弹性盒布局样式 使用CSS3布局技术设计适用移动需求的网页 13.1 多列布局 CSS3使用columns属性定义多列布局,用法如下 ...
- Protostuff序列化和反序列化
序列化和反序列化是在应对网络编程最常遇到的问题之一. 序列化就是将Java Object转成byte[]:反序列化就是将byte[]转成Java Object. 这里不介绍JDK serializab ...
- 一些常用的meta标签及其作用
声明文档使用的字符编码 <meta charset='utf-8'>优先使用 IE 最新版本和 Chrome <meta http-equiv="X-UA-Compat ...
- MySql 基础知识-常用命令及sql语句
一.常用mysql命令行命令 1,启动mysql服务 net start mysql. 停止mysql服务 net stop mysql 2,netstart -na|findstr 330 ...
- 《哈佛商业评论》2017年第5期:4星。成功CEO具有4种行为特质:果断、激励参与、主动适应、稳扎稳打。股东价值最大化的理念有重大缺陷。
老牌管理学杂志,每期都值得精度.本期几个比较重要的观点:谦逊的CEO能带来更好的业绩:飞利浦创新过度导致业绩下滑:股东最大化的理念有重大缺陷,后果之一是大宗股票的临时持有者可能干预公司事务,强迫公司采 ...
- Ubuntu Linux14 64位下在Android studio下用gradle编译Andrid项目时发生libz.so.1共享库找不到的解决方法。
---恢复内容开始--- 我在Ubuntu14 64为下安装了AS,但在用Gradle编译项目时总是报找不到 libz.so.1的错误. error while loading shared libr ...
- iOS Cell异步图片加载优化,缓存机制详解
最近研究了一下UITbleView中异步加载网络图片的问题,iOS应用经常会看到这种界面.一个tableView上显示一些标题.详情等内容,在加上一张图片.这里说一下这种思路. 为了防止图片多次下载, ...
- bdflush - 将dirty缓存写回到磁盘的核心守护进程
总览(SYNOPSIS) bdflush [opt] 描述(DESCRIPTION) bdflush 被用来启动核心守护进程将内存中的dirty缓存写到磁盘上.真正清洁工作是一个核心程序完成的. bd ...
- PHP 下基于 php-amqp 扩展的 RabbitMQ 简单用例 (五) -- 自动 ACK、手动 ACK、NACK
以 Direct 类型的 交换机和 Queue 的 get 方法为例. producer.php // 连接设置 $conConfig = [ 'host' => '127.0.0.1', 'p ...
- Spring Boot . 4 -- 定制 Spring Boot 配置 【2】
除了第一篇中使用 覆写的方式进行 自动配置的更改外,还可以通过 Spring Boot 中提供的 application.properties 文件改变应用的运行时配置.这种配置的方式粒度是非常精细的 ...