Java多线程:线程间通信方式
文章目录
- Java线程通信
- wait()、notify()、notifyAll()
- API说明
- 实现原理
- 代码实现
- await()、signal()、signalAll()
- API说明
- 实现原理
- 代码实现
- BlockingQueue
- API说明
- 实现原理
- 代码实现
Java线程通信
在Java中线程通信主要有以下三种方式:
wait()、notify()、notifyAll()
如果线程之间采用synchronized来保证线程安全,则可以利用wait()、notify()、notifyAll()来实现线程通信。
这三个方法都不是Thread类中所声明的方法,而是Object类中声明的方法。原因是每
个对象都拥有锁,所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作,Object源码方法如下:
并且因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。另外,这三个方法都是本地方法,并且被final修饰,无法被重写。
API说明
方法 | 说明 |
---|---|
wait() | 方法可以让当前线程释放对象锁并进入阻塞状态 |
notify() | 方法用于唤醒一个正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行 |
notifyAll() | 用于唤醒所有正在等待相应对象锁的线程,使它们进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行 |
实现原理
每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了已就绪(将要竞争锁)的线程,阻塞队列存储了被阻塞的线程。当一个阻塞线程被唤醒后,才会进入就绪队列,进而等待CPU的调度。反之,当一个线程被wait后,就会进入阻塞队列,等待被唤醒。
代码实现
案例步骤:通信是在不同线程间的通信,一个线程处于wait状态阻塞等待被唤醒,另一个线程通过notify或者notifyAll唤醒,当前的唤醒操作必须是作用与同一个对象,注意在进行唤醒和阻塞时必须要加锁的,加锁需要使用synchronized关键字。
代码如下:
package com.yyl.threadtest.communication;class WaitDemo extends Thread{private Object obj;public WaitDemo(Object obj) {this.obj = obj;}@Overridepublic void run() {synchronized (obj) {System.out.println(Thread.currentThread().getName() + "WaitDemo执行开始================");try {// 调用wait方法阻塞线程执行System.out.println(Thread.currentThread().getName() + "WaitDemo进行等待================");obj.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "WaitDemo执行结束================");}}
}class NotifyDemo extends Thread{private Object obj;public NotifyDemo(Object obj) {this.obj = obj;}@Overridepublic void run() {synchronized (obj) {System.out.println(Thread.currentThread().getName() + "NotifyDemo执行开始================");try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + "NotifyDemo 睡眠1s后进行通知================");} catch (InterruptedException e) {e.printStackTrace();}obj.notify(); //调用notify方法唤醒阻塞线程System.out.println(Thread.currentThread().getName() + "NotifyDemo执行结束================");}}
}public class WaitNotifyTest {public static void main(String[] args) {Object object = new Object();WaitDemo waitDemo = new WaitDemo(object);NotifyDemo notifyDemo = new NotifyDemo(object);waitDemo.setName("WaitDemo线程");notifyDemo.setName("NotifyDemo线程");waitDemo.start();notifyDemo.start();}
}
运行结果:
await()、signal()、signalAll()
如果线程之间采用Lock来保证线程安全,则可以利用await()、signal()、signalAll()来实现线程通信。
这三个方法都是Condition接口中的方法,该接口是在Java 1.5中出现的,它用来替代传统的wait+notify实现线程间的协作,它的使用依赖于 Lock。相比使用wait+notify,使用Condition的await+signal这种方式能够更加安全和高效地实现线程间协作。
Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition() 。 必须要注意的是,Condition 的 await()/signal()/signalAll() 使用都必须在lock保护之内,也就是说,必须在lock.lock()和lock.unlock之间才可以使用。
事实上,await()/signal()/signalAll() 与 wait()/notify()/notifyAll()有着天然的对应关系。
- Conditon中的await()对应Object的wait(),
- Condition中的signal()对应Object的notify()
- Condition中的signalAll()对应Object的notifyAll()
所以他们的API是类似的:
API说明
方法 | 说明 |
---|---|
await() | 方法可以让当前线程释放lock锁并进入阻塞状态。 |
signal() | 方法用于唤醒一个正在等待相应lock锁的线程,使其进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行。 |
signalAll() | 用于唤醒所有正在等待相应lock锁的线程,使它们进入就绪队列,以便在当前线程释放锁后竞争锁,进而得到CPU的执行。 |
lock.newCondition()() | 用于获得Condition对象 |
实现原理
内容过多,参考我的另一篇博文:https://blog.csdn.net/weixin_45525272/article/details/127741915
代码实现
package com.yyl.threadtest.communication;import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;class AwaitDemo extends Thread {private ReentrantLock lock;private Condition condition;public AwaitDemo(ReentrantLock lock, Condition condition) {this.lock = lock;this.condition = condition;}@Overridepublic void run() {lock.lock();System.out.println(Thread.currentThread().getName() + "AwaitDemo 执行开始================");try {// 调用wait方法阻塞线程执行System.out.println(Thread.currentThread().getName() + "AwaitDemo 进行等待================");condition.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "AwaitDemo 执行结束================");lock.unlock();}
}class SignalDemo extends Thread {private ReentrantLock lock;private Condition condition;public SignalDemo(ReentrantLock lock, Condition condition) {this.lock = lock;this.condition = condition;}@Overridepublic void run() {lock.lock();System.out.println(Thread.currentThread().getName() + "SignalDemo 执行开始================");try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + "SignalDemo 睡眠1s后进行通知================");} catch (InterruptedException e) {e.printStackTrace();}condition.signal(); //调用 signal 方法唤醒阻塞线程System.out.println(Thread.currentThread().getName() + "SignalDemo 执行结束================");lock.unlock();}
}public class AwaitSignalTest {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Condition condition = lock.newCondition();AwaitDemo awaitDemo = new AwaitDemo(lock,condition);SignalDemo signalDemo = new SignalDemo(lock,condition);awaitDemo.setName("AwaitDemo线程");signalDemo.setName("SignalDemo线程");awaitDemo.start();signalDemo.start();}
}
运行结果如下:
BlockingQueue
Java 5提供了一个BlockingQueue接口,虽然BlockingQueue也是Queue的子接口,但它的主要用途并不是作为容器,而是作为线程通信的工具。BlockingQueue具有一个特征:当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞。
程序的两个线程通过交替向BlockingQueue中放入元素、取出元素,即可很好地控制线程的通信。线程之间需要通信,最经典的场景就是生产者与消费者模型,而BlockingQueue就是针对该模型提供的解决方案。
API说明
核心方法:
方法 | 说明 |
---|---|
put(anObject) | 将参数放入队列,如果放不进去会阻塞 |
take() | 取出第一个数据,取不到会阻塞 |
其他方法(与List类似)
方法\处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
检查方法 | element() | peek() | 不可用 | 不可用 |
多线程一般多用put与take
实现原理
内容过多,参考我的另一篇博文:https://blog.csdn.net/weixin_45525272/article/details/127741915
代码实现
class Demo02 {public static void main(String[] args) throws Exception {// 创建阻塞队列的对象,容量为 1ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);// 存储元素arrayBlockingQueue.put("糖果");// 取元素System.out.println(arrayBlockingQueue.take());System.out.println(arrayBlockingQueue.take()); // 取不到会阻塞System.out.println("程序结束了");}
}
程序没有结束,他会一直读取阻塞队列
Java多线程:线程间通信方式相关推荐
- Java 如何线程间通信,面试被问哭。。。
Java 如何线程间通信,曾经小编面试被问哭的一道题.. 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及 ...
- android线程间通信的几种方法_Android进程间和线程间通信方式
进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 ...
- Android 进程间通信方式和线程间通信方式
1.进程和线程 进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独 ...
- linux的进程与线程通信方式,Linux的进程/线程间通信方式总结
Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...
- 多线程-线程间通信-多生产者多消费者示例
1.多线程-线程间通信-多生产者多消费者问题 多生产者和多消费者.等待唤醒机制. 产生了两个问题: 1.出现了多次连续生产,未消费,或者一个商品被消费多次. 解决:必须要--------每一个被唤醒的 ...
- Java多线程——线程的优先级和生命周期
Java多线程--线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...
- Linux的进程/线程间通信方式总结
Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...
- java多线程 线程安全_Java中的线程安全
java多线程 线程安全 Thread Safety in Java is a very important topic. Java provides multi-threaded environme ...
- java多线程-线程的停止【interrupt】
java多线程-线程的停止 文章目录 java多线程-线程的停止 线程停止的原理 如何正确停止线程 在普通情况下停止线程 代码展示 在阻塞情况下停止线程 代码展示 线程在每次迭代后都阻塞 代码展示 停 ...
- Java 多线程线程安全(面试概念解答二)
Java 多线程线程安全 什么是线程安全? 为什么有线程安全问题? 线程安全解决办法? 同步代码块 同步函数 静态同步函数 多线程死锁 多线程的三大特性 原子性 可见性 有序性 Java内存模型 Vo ...
最新文章
- 生成树的概念,最小生成树Prim算法 Kruskal算法
- 用Ghost几秒钟快速格式化120G大硬盘
- PIX配置手册三(nat)
- 【Python项目实战】提取.docx文件中的图片并保存到指定的文件夹
- racte margin 居中 失效_上干货,微信用情侣签名她肯定很开心,微信个性签名居中隐藏技巧...
- 实例化vue对象 绑定子组件_Vue-双向数据绑定
- amd建议超频吗_Amd 超频简单说明书——CPU篇
- 鸿蒙系统有无隐私空间,华为鸿蒙OS系统有隐私空间功能吗 华为p40使用鸿蒙系统体验评测...
- web可用性测试_Web开发人员和设计人员的最佳可用性测试工具
- Java中 volatile 关键字的最全总结,赶快给自己查缺补漏吧!
- 财务报表“难看”,怎么解决?
- 7教程统计意义_学渣的医学统计学自救笔记(一)
- 三维重建:三维重建技术概述
- CLion设置背景图片(Pycharm等)
- 【算法学习】【图像增强】【Retinex】源码运行
- 阿姆达尔定律(Amdahl’s Law) 计算
- 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
- Redis实现分布式session功能的共享
- Hibernate二级缓存——SessionFactory
- 原理图学习(点读笔调试)