线程通信(如 线程执行先后顺序,获取某个线程执行的结果等)有多种方式:

  • 文件共享 线程1 --写入--> 文件 < --读取-- 线程2
  • 网络共享
  • 变量共享 线程1 --写入--> 主内存共享变量 < --读取-- 线程2
  • jdk提供的线程协调API suspend/resume wait/notify park/unpark。

线程协作 - JDK API

线程协作的典型场景:生产者-消费者 模型(线程阻塞、线程唤醒)

如:线程1去卖包子,没有包子,则不再执行,线程2生产包子,通知线程1继续执行

API - 被弃用 suspend/resume

suspend挂起目标线程,resume恢复线程执行。

如下正常情况:

import java.util.concurrent.locks.LockSupport;

public class ThreadInteration {
public static Object baozidian =null;
public static void main(String[] args) throws InterruptedException {
new ThreadInteration().suspendResumeTest();
}
/**
* 使用弃用的API suspend和resume 来挂起目标线程和恢复线程执行
* 这两个api容易写出死锁的代码。
* 1,使用同步锁的时候,因为suspend不会释放锁,这样会导致死锁。
* 2,suspend 和 resume 的执行顺序颠倒,会导致死锁。
*/
//正常suspend 和 resume
public void suspendResumeTest() throws InterruptedException {
Thread consumerThread =new Thread(()->{
if (baozidian==null){
System.out.println("1 进入等待,线程被挂起");
Thread.currentThread().suspend();
System.out.println("线程被唤醒了");
}
System.out.println("3 买到包子了,回家!");
});
consumerThread.start();
Thread.sleep(3000L);
//生产者创建
baozidian=new Object();
consumerThread.resume();
System.out.println("2 通知消费者,消费者线程被唤醒");
}
}

死锁情况:

import java.util.concurrent.locks.LockSupport;

public class ThreadInteration {
public static Object baozidian =null;
public static void main(String[] args) throws InterruptedException {
new ThreadInteration().suspendResumeDeadLockTest();
// new ThreadInteration().suspendResumeDeadLockTest2();
} /**使用同步锁导致死锁,suspend和resume不会像wait一样释放锁**/
public void suspendResumeDeadLockTest() throws InterruptedException {
//创建线程
Thread consumerThread=new Thread(()->{
if (baozidian==null){//如果没有包子,就进入等待
//当前线程拿到锁,线程被挂起
synchronized (this){
System.out.println("1 进入等待,线程被挂起");
Thread.currentThread().suspend();
System.out.println("线程被唤醒了");
} }
System.out.println("3 买完包子,回家");
});
consumerThread.start();
Thread.sleep(2000L);
//产生包子
baozidian=new Object();
//争取到锁后,再恢复consumerThread()。
synchronized (this){
consumerThread.resume();
}
System.out.println("2 通知消费者,消费者线程被唤醒");
} /** 由于suspend/resume的调用顺序,导致程序永久死锁 **/
public void suspendResumeDeadLockTest2() throws InterruptedException {
Thread consumerThread=new Thread(()->{
if (baozidian==null){
System.out.println("1 进入线程,线程被挂起");
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里的suspend是运行在resume之后
Thread.currentThread().suspend();
System.out.println("线程被唤醒了");
}
System.out.println("3 买完包子,回家");
});
consumerThread.start();
Thread.sleep(2000L);
baozidian=new Object();
consumerThread.resume();
System.out.println("2 通知消费者,消费者线程被唤醒");
consumerThread.join();
}
}

API - wait/notify

import java.util.concurrent.locks.LockSupport;

public class ThreadInteration {
public static Object baozidian =null;
public static void main(String[] args) throws InterruptedException {
new ThreadInteration().waitNotifyTest();
// new ThreadInteration().waitNotifyDeadLockTest();
}
/**
* API推荐的 wait/notify 机制来挂起线程和唤醒线程
* 这些方法一定要是在同一锁对象的持有者线程调用。也就是写在同步代码块里面,否则会抛出IllegalMonitorStateException.
* wait方法就是将线程等待,调用wait就是把对象加入到 等待集合 中。并且放弃当前持有的锁对象
* notify/notify唤醒一个或者所有正在等待这个对象锁的进程。
*
* wait虽然会释放锁,但是对调用的顺序有要求。如果notify先与wait调用,线程会一直处于waiting状态。
*/
public void waitNotifyTest() throws InterruptedException {
new Thread(()->{
if (baozidian==null){
System.out.println("1 进入等待,线程将会被挂起");
synchronized (this){//顺序1 获取到锁
try {
this.wait();//顺序2 线程挂起,释放了锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程被唤醒了"); }
System.out.println("3 买到包子,回家");
}).start(); Thread.sleep(2000L);
baozidian=new Object();
synchronized (this){//顺序3 主线程拿到了锁
this.notify();//顺序4 主线程进行唤醒
}
System.out.println("2 通知消费者,消费者线程被唤醒");
} public void waitNotifyDeadLockTest() throws InterruptedException {
new Thread(()->{
if (baozidian==null){
try {
Thread.sleep(5000L);//顺序1
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1 进入等待,线程被挂起");
synchronized (this){
try {
this.wait();//顺序4 先唤醒了,再进行休眠。导致死锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程被唤醒了");
}
System.out.println("3 买到包子,回家");
}).start(); Thread.sleep(2000L);//顺序2
baozidian=new Object();
synchronized (this){
this.notify();//顺序3
}
System.out.println("2 通知消费者,消费者线程被唤醒");
}
}

API - park/unpark

这个是许可模式,同步代码中不能主动释放锁,许可模式是一个标记位,不能叠加

import java.util.concurrent.locks.LockSupport;
public class ThreadInteration {
public static Object baozidian =null;
public static void main(String[] args) throws InterruptedException {
new ThreadInteration().parkUnparkTest();
// new ThreadInteration().parkUnparkDeadLockTest();
}
/**
* 线程调用 park 表示线程等待"许可",unpack 表示为指定的线程提供"许可"
* park和 unpark 对调用顺序没有要求。
* 多次调用 unpack 之后,再调用 pack ,线程会立即执行。
* 但是这个"许可"不是叠加的,是一个标志位。
* 例如多次调用了 unpack 这个时候也只有一个"许可",这个时候调用一次 park 就会拿到"许可"直接运行。后面的 park 还是得继续等待
*/
public void parkUnparkTest() throws InterruptedException {
Thread consumerThread=new Thread(()->{
if (baozidian==null){
// try {
// Thread.sleep(5000L);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("1 进入等待,线程被挂起");
LockSupport.park();
System.out.println("线程被唤醒了");
}
System.out.println("3 买到包子,回家");
});
consumerThread.start();
Thread.sleep(2000L);
baozidian=new Object();
LockSupport.unpark(consumerThread);
System.out.println("2 通知消费者,消费者线程被唤醒");
} /** park/unpark 不能自动释放锁**/
public void parkUnparkDeadLockTest() throws InterruptedException {
Thread consumerThread=new Thread(()->{
if (baozidian==null){
System.out.println("1 进入等待,线程被挂起");
synchronized (this){//这个时候park获取了锁,然后挂起了。没有及时释放锁导致后面的unpark获取不到锁,就执行不了unpark
LockSupport.park();
}
System.out.println("线程被唤醒了");
}
System.out.println("3 买到包子,回家");
});
consumerThread.start();
Thread.sleep(2000L);
baozidian=new Object();
synchronized (this){
LockSupport.unpark(consumerThread);
}
System.out.println("2 通知消费者,消费者线程被唤醒");
}
}

伪唤醒

/**
* 伪唤醒:前面使用了if (baozidian==null) 来判断是否进入等待状态,是错误的。是指并非由notify/unpack来唤醒的,由更底层的原因被唤醒。
* 官方建议使用while (baozidian==null) 来判断是否进入等待状态。
* 因为:处于底层的线程可能会收到错误警报和伪唤醒,如果不在循环中检查,程序可能会在没有满足条件的情况下退出
* 解决方案就是将上面的if全部改成while
*/
public void waitNotifyGoodTest() throws InterruptedException {
new Thread(()->{
synchronized (this){
//将while放入同步锁中判断
while (baozidian==null){
System.out.println("1 进入等待,线程将会被挂起");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程被唤醒了"); }
System.out.println("3 买到包子,回家");
}).start(); Thread.sleep(2000L);
baozidian=new Object();
synchronized (this){
this.notify();
}
System.out.println("2 通知消费者,消费者线程被唤醒");
}

多线程 - 线程通信 suspend-resume wait-notify park-unpark 伪唤醒的更多相关文章

  1. jdk提供的线程协调API suspend/resume wait/notify park/unpark

    线程通信(如 线程执行先后顺序,获取某个线程执行的结果等)有多种方式: 文件共享 线程1 --写入--> 文件 < --读取-- 线程2 网络共享 变量共享 线程1 --写入--> ...

  2. java多线程-线程通信

    线程通信的目标是使线程间能够互相发送信号.另一方面,线程通信使线程能够等待其他线程的信号. 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线 ...

  3. JAVA基础知识之多线程——线程通信

    传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题. wait():释放当前线程的同步监视控制器,并 ...

  4. java多线程——线程通信

    一.线程通信目标 1.线程通信的目标是使线程间能够互相发送信号 2.线程通信使线程能够等待其他线程的信号 二.几种方式 1.通过共享对象 2.忙等待 线程 B 运行在一个循环里,以等待信号 (不释放c ...

  5. java 之 wait, notify, park, unpark , synchronized, Condition

    1. wait notify /** * 解释: 唤醒一个等待monitor的线程, 如果有多个线程在等待,会唤醒一个. * 一个线程在等待monitor是由Object.wait引起的 * 获取一个 ...

  6. 十八 线程暂停 suspend/ resume

    1  Suspend.resume 的缺点1 :独占!  线程执行到同步块中,如果线程暂停了,不会释放锁. 比如,比如System.out.println()方法就是一个同步方法, 如果线程调用Sys ...

  7. 为什么线程通信的方法 wait(), notify()和 notifyAll()被定 义在 Object 类里?

    Java 的每个对象中都有一个锁(monitor,也可以成为监视器) 并且 wait(),notify() 等方法用于等待对象的锁或者通知其他线程对象的监视器可用.在 Java 的线程中 并没有可供任 ...

  8. C++多线程の线程通信future,promise,async

  9. 线程sleep,wait,notify,join,yield方法解析

    线程的五种状态 线程从创建到销毁一般分为五种状态,如下图: 1) 新建 当用new关键字创建一个线程时,就是新建状态. 2) 就绪 调用了 start 方法之后,线程就进入了就绪阶段.此时,线程不会立 ...

随机推荐

  1. Block代码块中使用局部变量注意点

    第一次写代码遇到报这个错,实在是想不通为什么,按常理应该是不会有问题,报错的呀??纠结了一会之后只好仔细查看报错原因咯,原来是: 当我们在block代码块中使用局部变量时,就会很容易出现如图的错误. ...

  2. Python核心编程练习题

    1.输入一个数值,判断是否为正数,负数,小数,以及字符串 import re def is_number(num): pattern = re.compile(r'^[-+]?[-0-9]\d*\.\ ...

  3. swoole前置基础知识 进程间通信

    进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息. IPC的方式通常有管道(包括无名管道和命名管道).消息队列.信号量.共享存储.Socket ...

  4. C学习笔记-内存管理

    作用域 一个C语言变量的作用域可以是代码块 作用域,函数作用域或者文件作用域 代码块是{}之间的一段代码 同一个代码块不可以有重名变量 auto自动变量 一般情况下代码块内部定义的变量都是自动变量 也 ...

  5. PHP反序列化进阶寻找和构造

    POP链的构造 如果关键代码不在魔术方法中,而是在一个类的普通方法中. 这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来 <?phpclass lemon {    protec ...

  6. 【转帖】超能课堂(188) WiFi 6凭什么可以如此“六”?

    https://www.expreview.com/69155.html 不明觉厉 这些东西 自己理解的还是少呢 电脑硬件可能一年甚至不到一年就会开始更新换代,但是路由器就不一样,它们的更新换代往往是 ...

  7. Oracle-DML- insert & update & delete

    说明:语句中说到的“表”,以及表中有哪些“列”自行脑补......重要的是理解概率,能看懂语句代表的含义就OK~ DML-数据操作语句: 1. insert 新增 /*insert into 表名va ...

  8. robot-framework 利用evaluate关键字生成随机数

    robot-framework 利用evaluate关键字生成随机数 最近用RF(robot-framework简称)操作MangoDB,需要直接将数据写到数据库里,又不想每次写的数据完全相同,就想到 ...

  9. ASP.NET Core中使用EF Core(MySql)Database First

    ⒈创建数据库,在数据中执行以下脚本. CREATE DATABASE Blogging; USE Blogging; CREATE TABLE Blog ( BlogId int not null P ...

  10. CNN中feature map、卷积核、卷积核的个数、filter、channel的概念解释

    CNN中feature map.卷积核.卷积核的个数.filter.channel的概念解释 参考链接: https://blog.csdn.net/xys430381_1/article/detai ...