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 ...
随机推荐
- Python集合&字典
Python集合&字典 @ 目录 字典 字典的几种定义方式 第一种 第二种 第三种 字典的一些基本操作 通过key得到value 增加字典键值对 删除字典键值对 格式化操作 清空字典 复制字典 ...
- BZOJ 3729 - Gty的游戏(Staircase 博弈+时间轴分块)
题面传送门 介于自己以前既没有写过 Staircase-Nim 的题解,也没写过时间轴分块的题解,所以现在就来写一篇吧(fog 首先考虑最极端的情况,如果图是一条链,并且链的一个端点是 \(1\),那 ...
- python18协程
协程是我们自己调度的 进程是系统调度的协程切换很少开销 python3.5之前的实现方法 def yield_test(): """实现协程函数""& ...
- 38- Majority Element
Majority Element My Submissions QuestionEditorial Solution Total Accepted: 110538 Total Submissions: ...
- OpenSSH 密码和公钥认证原理探究
目录 配置和保护SSH H3 - 使用SSH 访问远程命令行 H4 - 什么是OpenSSH ? H4 - 登录方式: H4 - 登录并执行临时命令: H4 - 查看登录用户 H4 - 登录原理 密码 ...
- 学习java的第七天
一.今日收获 1.看完全学习手册上java关键字与标识符两节 2.了解了java的关键字与标识符 二.今日难题 1.基本都理解 三.明日目标 1.继续看完全学习手册上的内容 2.加油!
- 大厂高频面试题Spring Bean生命周期最详解
Spring作为当前Java最流行.最强大的轻量级框架.Spring Bean的生命周期也是面试高频题,了解Spring Bean周期也能更好地帮助我们解决日常开发中的问题.程序员应该都知道Sprin ...
- 基于 Golang 构建高可扩展的云原生 PaaS(附 PPT 下载)
作者|刘浩杨 来源|尔达 Erda 公众号 本文整理自刘浩杨在 GopherChina 2021 北京站主会场的演讲,微信添加:Erda202106,联系小助手即可获取讲师 PPT. 前言 当今时 ...
- 从源码看RequestMappingHandlerMapping的注册与发现
1.问题的产生 日常开发中,大多数的API层中@Controller注解和@RequestMapping注解都会被使用在其中,但是为什么标注了@Controller和@RequestMapping注解 ...
- 100个Shell脚本——【脚本5】数字求和
[脚本5]数字求和 编写shell脚本,要求输入一个数字,然后计算出从1到输入数字的和,要求,如果输入的数字小于1,则重新输入,直到输入正确的数字为止,示例: 一.脚本 #!/bin/bash whi ...