文章目录

  • 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多线程:线程间通信方式相关推荐

  1. Java 如何线程间通信,面试被问哭。。。

    Java 如何线程间通信,曾经小编面试被问哭的一道题.. 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及 ...

  2. android线程间通信的几种方法_Android进程间和线程间通信方式

    进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 ...

  3. Android 进程间通信方式和线程间通信方式

    1.进程和线程 进程:是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程:是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独 ...

  4. linux的进程与线程通信方式,Linux的进程/线程间通信方式总结

    Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...

  5. 多线程-线程间通信-多生产者多消费者示例

    1.多线程-线程间通信-多生产者多消费者问题 多生产者和多消费者.等待唤醒机制. 产生了两个问题: 1.出现了多次连续生产,未消费,或者一个商品被消费多次. 解决:必须要--------每一个被唤醒的 ...

  6. Java多线程——线程的优先级和生命周期

    Java多线程--线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...

  7. Linux的进程/线程间通信方式总结

    Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...

  8. java多线程 线程安全_Java中的线程安全

    java多线程 线程安全 Thread Safety in Java is a very important topic. Java provides multi-threaded environme ...

  9. java多线程-线程的停止【interrupt】

    java多线程-线程的停止 文章目录 java多线程-线程的停止 线程停止的原理 如何正确停止线程 在普通情况下停止线程 代码展示 在阻塞情况下停止线程 代码展示 线程在每次迭代后都阻塞 代码展示 停 ...

  10. Java 多线程线程安全(面试概念解答二)

    Java 多线程线程安全 什么是线程安全? 为什么有线程安全问题? 线程安全解决办法? 同步代码块 同步函数 静态同步函数 多线程死锁 多线程的三大特性 原子性 可见性 有序性 Java内存模型 Vo ...

最新文章

  1. 生成树的概念,最小生成树Prim算法 Kruskal算法
  2. 用Ghost几秒钟快速格式化120G大硬盘
  3. PIX配置手册三(nat)
  4. 【Python项目实战】提取.docx文件中的图片并保存到指定的文件夹
  5. racte margin 居中 失效_上干货,微信用情侣签名她肯定很开心,微信个性签名居中隐藏技巧...
  6. 实例化vue对象 绑定子组件_Vue-双向数据绑定
  7. amd建议超频吗_Amd 超频简单说明书——CPU篇
  8. 鸿蒙系统有无隐私空间,华为鸿蒙OS系统有隐私空间功能吗 华为p40使用鸿蒙系统体验评测...
  9. web可用性测试_Web开发人员和设计人员的最佳可用性测试工具
  10. Java中 volatile 关键字的最全总结,赶快给自己查缺补漏吧!
  11. 财务报表“难看”,怎么解决?
  12. 7教程统计意义_学渣的医学统计学自救笔记(一)
  13. 三维重建:三维重建技术概述
  14. CLion设置背景图片(Pycharm等)
  15. 【算法学习】【图像增强】【Retinex】源码运行
  16. 阿姆达尔定律(Amdahl’s Law) 计算
  17. 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
  18. Redis实现分布式session功能的共享
  19. Hibernate二级缓存——SessionFactory
  20. 原理图学习(点读笔调试)

热门文章

  1. 两行命令查看电脑wifi密码
  2. python画出来的老鼠那么萌
  3. python 深度 视差 计算_NCC视差匹配计算视差图(python)
  4. 三星健康权限_三星健康能做什么?
  5. 做微信直播怎么吸引更多的人气
  6. android P lightservice 移植调试增加状态指示灯 保存状态到设置开关 --驱动 hal lib HIDL service framework settingsprovide
  7. 攻防世界 Web baby_web
  8. 君君学Linux设备驱动第一天之概述及开发环境搭建
  9. DataCamp:用pandas进行数据处理
  10. 质量管理体系之测试用例