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 ...
随机推荐
- System.Web.Optimization
项目中引用了 System.Web.Optimization 这个程序集,缺少程序集会报错: 命名空间"System.Web"中不存在类型或命名空间名"Optimizat ...
- 一个 Linux 后台程序编程案例分析
Linux 下的一个进程打开一个日志文件,不定期地往该文件里写入日志.此时可以在控制台使用 mv 命令给该日志文件改个名字或者用 rm 命令把这个日志文件删除掉.Linux 下是允许这么干的!对于改日 ...
- setoolkit的钓鱼实验
1.在kali中打开setoolkit 2.在菜单中选择第一个进入社会工程学攻击 3.选择第二个模块属于网站攻击向量 4.选择第五个模块,进行web劫持攻击 5.选择第二个,进行网站克隆 6.发现访问 ...
- 浅谈Java和JavaScript中变量和数据类型的区别
对于一门编程语言的学习,如果第一步是安装环境,那么第二步一定是学习这门语言的基本规则,变量和数据类型则首当其冲 JavaScipt作为一个蹭Java热度而命名的语言,在很多方面和Java也有一定的相似 ...
- 洛谷 P5224 - Candies(循环卷积)
洛谷题面传送门 一道题解长度大概不到 1k 的题,可还是决定写篇题解,因为自己没有做出来( \(1004535809\) 好评( 首先这个 \(\equiv m\pmod{k}\) 有点把我们往单位根 ...
- 【5】肿瘤DNA甲基化数据分析原理及流程
目录 导论 DNA甲基化基本概论 检测DNA甲基化的方法 DNA甲基化数据分析流程及方法 DNA甲基化在肿瘤研究中的应用 导论 表观遗传:非DNA决定的基因表达,或表型改变中可遗传因素的研究 DNA水 ...
- 睡眠或者重启windows,无法ssh连接或者pingVMware的虚机
睡眠后无法直接ssh重连VMware主机问题 这个问题我在win8上出现过,win10看了下同事可以正常连,不知道其他有没有问题. 解决方法: 1.关闭vmnet8和vmnet0里面的 npcap 功 ...
- gcc 引用math 库 编译的问题 解决方法
1.gcc app.c -lm 其中lm表示的是连接 m forlibm.so / libm.a表示你想要的库 abc for libabc.so / libabc.a 其中.a表示的是静态链接库 . ...
- 进程和线程操作系统转载的Mark一下
https://www.cnblogs.com/leisure_chn/p/10393707.html Linux的进程线程及调度 本文为宋宝华<Linux的进程.线程以及调度>学习笔记. ...
- A Child's History of England.37
Many other noblemen repeating and supporting this when it was once uttered, Stephen and young Planta ...