java 多线程:线程通信-等待通知机制wait和notify方法;(同步代码块synchronized和while循环相互嵌套的差异);管道通信:PipedInputStream;PipedOutputStream;PipedWriter; PipedReader
1、等待通知机制:
wait和notify方法:
/**
* @ClassName LockThreadWaitNotify
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/23.
*/
public class LockThreadWaitNotify {
private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(){
@Override
public void run() {
synchronized (lock){
System.out.println(getName() + "开始等待.....");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + "等待结束...");
}
}; threadA.start();//放在threadB定义之前执行,可以确保先获取到锁...
Thread threadB = new Thread(){
@Override
public void run() {
/**
* 代码块作为单独的子线程执行,可以通知到threadA,如果放到main方法执行则大概率比子线程先获取到锁
*/
synchronized (lock){
try {
Thread.sleep(300);
System.out.println(Thread.currentThread().getName() + "发指令去唤醒threadA");
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
// threadA.start();//放在这里执行,有可能threadB先获取到锁...
threadB.start();
}
}


示例:从0开始计算闰年输出闰年年份
/**
* @ClassName LockThreadWaitNotify
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/23.
*/
public class LockThreadWaitNotify2 {
private static Object lock = new Object();
private static volatile int num=0; public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(){
@Override
public void run() {
synchronized (lock){
while (true){
num++;
//计算到闰年就暂停线程
if(num % 4 == 0 && num % 100 != 0){
System.out.println(getName() + "开始等待.....");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}; threadA.start();//放在threadB定义之前执行,可以确保先获取到锁...
Thread threadB = new Thread(){
@Override
public void run() {
/**
* 代码块作为单独的子线程执行,可以通知到threadA,如果放到main方法执行则大概率比子线程先获取到锁
*/
while (true){
/**System.out.println("num++ 值为:" + num);
* 这个获取值要再获取锁代码框之外,因为如果放在synchronized里面
* 会导致释放的锁立刻又被重新获取到,导致threadA无法再次获取到锁
*/
System.out.println("闰年为:" + num);
synchronized (lock){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "发指令去唤醒threadA");
lock.notify();
}
}
}
};
// threadA.start();//放在这里执行,有可能threadB先获取到锁...
threadB.start();
}
}

2、同步代码块 和while循环相互嵌套的差异
while循环在同步代码块内部:
- * while循环在同步代码块内部。锁的定义只被定义了一次,循环是非常快的,
- * 所以lock.notify();后必须要马上lock.wait();把锁抛出去,
- * 否则会因为while循环非常消耗CPU,导致锁瞬间就被重新获取回来,threadA还是获取不到
同步代码块在while循环内部:
- 每次循环完毕都等于自动释放了锁,无需明确指定lock.wait
示例代码:
/**
* @ClassName LockThreadWaitNotify
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/23.
*/
public class LockThreadWaitNotify3 {
private static Object lock = new Object();
private static volatile int num=0; public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(){
@Override
public void run() {
synchronized (lock){
while (true){
num++;
//计算到闰年就暂停线程
if(num % 4 == 0 && num % 100 != 0){
System.out.println(getName() + "开始等待.....");
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}; threadA.start();//放在threadB定义之前执行,可以确保先获取到锁...
Thread threadB = new Thread(){
@Override
public void run() {
/**
* 代码块作为单独的子线程执行,可以通知到threadA,如果放到main方法执行则大概率比子线程先获取到锁
*/
synchronized (lock) {
/**
* while循环在同步代码块内部。锁的定义只被定义了一次,循环是非常快的,
* 所以lock.notify();后必须要马上lock.wait();把锁抛出去,
* 否则会因为while循环非常消耗CPU,导致锁瞬间就被重新获取回来,threadA还是获取不到
*/
while (true){
System.out.println("闰年为:" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "发指令去唤醒threadA");
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
// threadA.start();//放在这里执行,有可能threadB先获取到锁...
threadB.start();
}
}

3、 线程生命周期

4、wait(等待) & interrupt(停止):

5、notifyAll()
6、wait(long):
7、过早通知问题:
如果notifyAll()、notify() 发生在wait()之前,则没有线程被唤醒。会导致僵死状态。
小练习:做馒头~吃馒头
import java.util.LinkedList;
import java.util.Random; /**
* @ClassName LockThreadWaitNotifyMantou
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/24.
*/
public class LockThreadWaitNotifyMantou {
public static void main(String[] args) {
LinkedList<String> mantous = new LinkedList<>();
Object lock = new Object();
Runnable product = () -> {
while (true){
synchronized (lock){
if (mantous.size() >= 10){
System.out.println(Thread.currentThread().getName()+ "说 库存:"+mantous.size() + "快来吃吧");
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
mantous.add("馒头"); //将指定的元素追加到此列表的末尾。
System.out.println(Thread.currentThread().getName() + "生产了一个馒头," + "库存:" + mantous.size());
lock.notifyAll();
try {
// lock.notifyAll();
// lock.wait(1);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Runnable custumer = () -> {
Random random = new Random();
synchronized (lock) {
while (true){
Integer buyMantou = Math.abs(random.nextInt()) % 5 + 1;
if (mantous.size() >= buyMantou){
//吃馒头
System.out.println(Thread.currentThread().getName() + "开始吃馒头");
try { //慢点吃,
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//吃
for (int i=0; i<buyMantou; i++){
mantous.poll();
}
//吃完了
System.out.println(Thread.currentThread().getName() + "吃了" + buyMantou + "个馒头,还剩" + mantous.size());
}else { //没得吃,不够吃
System.out.println(Thread.currentThread().getName() + "想吃" + buyMantou + "只有" +mantous.size() + "个了");
System.out.println(Thread.currentThread().getName() +":师傅快做馒头吧....");
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}; Thread thread = new Thread(product,"厨师1");
Thread thread1 = new Thread(product,"厨师2");
Thread thread2 = new Thread(custumer,"饭桶1");
Thread thread3 = new Thread(custumer,"饭桶2");
Thread thread4 = new Thread(custumer,"饭桶3");
thread1.start();
thread.start();
thread2.start();
thread4.start();
thread3.start();
}
}
管道通信:
在JAVA语言中,提供了各种各样的输入流/输出流 stream, 让我们可以很方便的进行数据操作。其中管道流是一种特殊的流。。专门用在不同线程之间直接传送数据一个线程从输出管道中写入数据,另外一个线程从数据管道中读取数据,而无需借助临时文件之类的东西。
字节流:
- PipedInputStream;PipedOutputStream;
- PipedWriter; PipedReader
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream; /**
* @ClassName ThreadConnectInStream
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/24.
*/
public class ThreadConnectInStream {
public static void main(String[] args) {
/**
* 演示字节流输入输出对接。
*/
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream();
//关联管道输入流输出流
try {
pipedInputStream.connect(pipedOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
Thread diaosi = new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
pipedOutputStream.write("女神好".getBytes());
pipedOutputStream.flush();
Thread.sleep(1000);
pipedOutputStream.write("女神sss好".getBytes());
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}finally {
try {
pipedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
Thread nvShen = new Thread(){
@Override
public void run() {
int len = -1;
byte[] bytes = new byte[1024];
try {
while ((len = pipedInputStream.read(bytes)) != -1) {
System.out.println("收到消息");
String string = new String(bytes,0,len);
System.out.println(string);
}
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
diaosi.start();
nvShen.start();
}
}
流不关闭则抛异常

字符流示例代码:
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedReader;
import java.io.PipedWriter; /**
* @ClassName ThreadConnectInWriter
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/24.
*/
public class ThreadConnectInWriter {
public static void main(String[] args) {
/**
* 字符输入输出流对接
*/
PipedWriter pwriter = new PipedWriter();
PipedReader pReader = new PipedReader();
try {
pReader.connect(pwriter);
} catch (IOException e) {
e.printStackTrace();
}
Thread writer = new Thread(){
@Override
public void run() {
try {
pwriter.write("我爱你");
Thread.sleep(1000);
pwriter.flush();
pwriter.write("真的");
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}finally {
try {
pwriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
Thread reader = new Thread(){
@Override
public void run() {
try {
int len = -1;
char[] chars = new char[1024] ;
while ((len = pReader.read(chars)) != -1){
String string = new String(chars,0,len);
System.out.println(string);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
pReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
writer.start();
reader.start();
}
}
java 多线程:线程通信-等待通知机制wait和notify方法;(同步代码块synchronized和while循环相互嵌套的差异);管道通信:PipedInputStream;PipedOutputStream;PipedWriter; PipedReader的更多相关文章
- java多线程系列(三)---等待通知机制
等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...
- Java多线程-----线程安全及解决机制
1.什么是线程安全问题? 从某个线程开始访问到访问结束的整个过程,如果有一个访问对象被其他线程修改,那么对于当前线程而言就发生了线程安全问题: 如果在整个访问过程中,无一对象被其他线程修改,就是线程安 ...
- Java 线程间通信 —— 等待 / 通知机制
本文部分摘自<Java 并发编程的艺术> volatile 和 synchronize 关键字 每个处于运行状态的线程,如果仅仅是孤立地运行,那么它产生的作用很小,如果多个线程能够相互配合 ...
- 彻底理解线程同步与同步代码块synchronized
public class Demo { public static synchronized void fun1(){ } public synchronized void fun2(){ } pub ...
- 36. 解决线程问题方式一(同步代码块synchronized)
解决线程问题: 方式一:同步代码块(synchronized) 语法: synchronized ("锁对象") { //需要锁定的代码 } ...
- Java 同步代码块 - Synchronized Blocks
java锁实现原理: http://blog.csdn.net/endlu/article/details/51249156 The synchronized keyword can be used ...
- java多线层同时运行的解决,同步代码块synchronized
/* 多个线层同时操作一个数据 会导制数据超出 同步代码块 synchronized(对像) { 需要同步的代码 } */ class Do7 { public static void main(St ...
- Java并发编程(04):线程间通信,等待/通知机制
本文源码:GitHub·点这里 || GitEE·点这里 一.概念简介 1.线程通信 在操作系统中,线程是个独立的个体,但是在线程执行过程中,如果处理同一个业务逻辑,可能会产生资源争抢,导致并发问题, ...
- 【Java并发基础】使用“等待—通知”机制优化死锁中占用且等待解决方案
前言 在前篇介绍死锁的文章中,我们破坏等待占用且等待条件时,用了一个死循环来获取两个账本对象. // 一次性申请转出账户和转入账户,直到成功 while(!actr.apply(this, targe ...
随机推荐
- Springboot .properties或.yml配置文件读取pom.xml文件值
有时候配置文件需要读取pom文件配置<properties></properties>中间自定义属性值的时候可以用@@获取 例:@package.parameter@ 然后还需 ...
- SpringBoot集成邮件发送
一:简述 在日常中的工作中难免会遇到程序集成邮件发送功能.接收功能:此篇文章我将使用SpringBoot集成邮件发送功能和接收功能:若对邮件一些基本协议和发送流程不懂的请务必参考我之前写的博客或者浏览 ...
- 8.2 k8s 基于StatefulSet运行mysql 一主多从 ,数据通过pv/pvc结合NFS服务器持久化
1.准备mysql和xtrabackup镜像 下载mysql官方镜像并上传到本地harbor docker pull mysql:5.7 docker tag m ysql:5.7 192.168.1 ...
- 快速入门上手Markdown
第一次接触Markdown是写代码初期看很多大佬的github,他们的项目一定会有一份文件叫Readme.md的文件 他们由一些简单美观的符号和汉字字母组成,编译之后成为一篇简单直观的文档 深入了解之 ...
- ISIJ2021 游记
Day -419 ~ ? - 2020.5.8 ~ ? 咦?ISIJ2021 怎么会扯到 2020 年的事情呢? 好吧这似乎真的是一个长篇大论,事情真的要从那一天开始讲起-- 仍依稀记得 2020 年 ...
- ABC 210
A 按题意模拟. scanf("%lld%lld%lld%lld",&n,&a,&x,&y); std::cout<<n * x - ( ...
- [Cnoi2020]线性生物
期望入门题.但是我不会做. 考虑设\(E_{x\to{x+1}}\)为\(x\)到\(x+1\)点的期望步数. 则\(ans = \sum_{i = 0}^{n} E_{x\to{x+1}}\) 知\ ...
- 洛谷 P3714 - [BJOI2017]树的难题(点分治)
洛谷题面传送门 咦?鸽子 tzc 竟然来补题解了?incredible( 首先看到这样类似于路径统计的问题我们可以非常自然地想到点分治.每次我们找出每个连通块的重心 \(x\) 然后以 \(x\) 为 ...
- R 语言实战-Part 5-1笔记
R 语言实战(第二版) part 5-1 技能拓展 ----------第19章 使用ggplot2进行高级绘图------------------------- #R的四种图形系统: #①base: ...
- 在Linux下搭建nRF51822的开发烧写环境(makefile版)
http://www.qingpingshan.com/m/view.php?aid=394836