Java学习笔记46(多线程三:线程之间的通信)
多个线程在处理同一个资源,但是线程的任务却不相同,通过一定的手段使各个线程能有效地利用资源,
这种手段即:等待唤醒机制,又称作线程之间的通信
涉及到的方法:wait(),notify()
示例:
两个线程一个输入,一个输出
package demo;
public class Resource {
public String name;
public String sex;
}
输入线程:
package demo;
public class Input implements Runnable {
private Resource r = new Resource();
public void run() {
int i = 0;
while (true) {
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "李四";
r.sex = "女";
}
i++;
}
}
}
输出线程:
package demo;
public class Output implements Runnable {
private Resource r = new Resource();
public void run(){
while (true) {
System.out.println(r.name+"..."+r.sex);
}
}
}
测试类:
package demo;
public class ThreadDemo {
public static void main(String[] args) {
Input in = new Input();
Output out = new Output();
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
运行后却发现输出的都是null...null
因为输入线程和输出线程中创建的Resource对象使不同的
解决null问题:
package demo;
public class Input implements Runnable {
private Resource r;
public Input(Resource r){
this.r = r;
}
public void run() {
int i = 0;
while (true) {
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "李四";
r.sex = "女";
}
i++;
}
}
}
package demo;
public class Output implements Runnable {
private Resource r;
public Output(Resource r){
this.r = r;
}
public void run(){
while (true) {
System.out.println(r.name+"..."+r.sex);
}
}
}
package demo;
public class ThreadDemo {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
运行后又发现了另一个问题:
输出中含有:张三...女或者李四...男,性别出错
发生原因:
赋值完张三和男后,继续赋值李四和女,这时候还未还得及赋值女,就进入了输出线程,这时候就会输出李四...男
于是想到加上同步:
public void run() {
int i = 0;
while (true) {
synchronized (this) {
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "李四";
r.sex = "女";
}
i++;
}
}
}
public void run() {
while (true) {
synchronized (this) {
System.out.println(r.name + "..." + r.sex);
}
}
}
然而问题并没有解决:
原因:
这里的同步失去了作用,用到的不是一个锁
解决办法:
使用一个共同的锁即可
public void run() {
int i = 0;
while (true) {
synchronized (r) {
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "李四";
r.sex = "女";
}
i++;
}
}
}
public void run() {
while (true) {
synchronized (r) {
System.out.println(r.name + "..." + r.sex);
}
}
}
这时候就是正常的输出了
但是还是存在一个问题,我们希望的是张三和李四交错出现,一个张三一个李四,现在依然是随机出现的,大片的张三或李四
解决办法:
先让input线程赋值,然后让output线程输出,并且让输入线程等待,不允许再赋值李四,等待输出张三结束后,再允许李四赋值,依次下去
输入线程也需要同样的方式,输出完后要等待
这时候就需要用到等待唤醒机制:
输入:赋值后,执行方法wait()永远等待
输出:打印后,再输出等待之前,唤醒输入notify(),自己再wait()永远等待
输入:被唤醒后,重新赋值,必须notify()唤醒输出的线程,自己再wait()等待
依次循环下去
代码实现:
package demo;
public class Resource {
public String name;
public String sex;
public boolean flag = false;
}
package demo;
public class Input implements Runnable {
private Resource r;
public Input(Resource r) {
this.r = r;
}
public void run() {
int i = 0;
while (true) {
synchronized (r) {
if (r.flag) {
try {
r.wait();
} catch (Exception e) {
}
}
if (i % 2 == 0) {
r.name = "张三";
r.sex = "男";
} else {
r.name = "李四";
r.sex = "女";
}
r.flag = true;
r.notify();
}
i++;
}
}
}
package demo;
public class Output implements Runnable {
private Resource r;
public Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (r) {
if (!r.flag) {
try {
r.wait();
} catch (Exception e) {
}
}
System.out.println(r.name + "..." + r.sex);
r.flag = false;
r.notify();
}
}
}
}
package demo;
public class ThreadDemo {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
这时候就是张三李四交错输出了
完成
Java学习笔记46(多线程三:线程之间的通信)的更多相关文章
- Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)
多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...
- Java多线程编程-线程之间的通信
转载自:这里 学习了基础的线程知识 看到了 线程之间的通信 线程之间有哪些通信方式呢? 1.同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信. public ...
- 20145316许心远《Java学习笔记》第三周总结
20145316许心远<Java程序设计>第3周学习总结 教材学习内容总结 一.定义类: 类定义时使用class关键字 如果要将x绑定到新建的对象上,可以使用"="制定 ...
- 【原】Java学习笔记032 - 多线程
package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...
- Java学习笔记:多线程(二)
与线程生命周期相关的方法: sleep 调用sleep方法会进入计时等待状态,等待时间到了,进入就绪状态. yield 调用yield方法会让别的线程执行,但是不确保真正让出.较少使用,官方注释都说 ...
- java之等待唤醒机制(线程之间的通信)
线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消 ...
- 10月9日Android学习笔记:活动与服务之间的通信
最近在照着<第一行代码>这本书来学安卓,顺便记下笔记.主要的内容是Android中服务的第二种启动方式,通过活动绑定服务来启动服务,实现活动与服务之间的通信. 一. 首先创建一个服务类 p ...
- Java学习笔记:多线程(一)
Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...
- Java 学习笔记(11)——多线程
Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率. 使用多线程的方法 创建Thread类的子类,并重写run方法,在需要启动 ...
随机推荐
- tomcat8做成windows服务
- typedef typename
所以根据上述两条分析, typedef typename RefBase::weakref_type weakref_type; 语句的真是面目是: typedef创建了存在类型的别名,而typen ...
- cleos
[cleos] 1.在.bashrc中加入以下代码,方便直接使用 cleos,7777是nodeos端口,5555是keosd端口. alias cleos='docker exec -it eosi ...
- POJ-3268.SilverCowParty.(最短路 + 图的转置)
本题思路:对原图和原图的逆图分别用一次最短路,找出最大值即可. 一开始是我是对每个顶点spfa搜了一波,结果判题时间巨长,还好这个题的数据量不是很大,所以就用了另一种思路. 参考代码:spfa单结点爆 ...
- Unity 2018 By Example 2nd Edition
Unity is the most exciting and popular engine used for developing games. With its 2018 release, Unit ...
- EasyUI datagrid单元格文本超出显示省略号,鼠标移动到单元格显示文本
nowrap : true; 是前提 $('#×××').datagrid({ nowrap : true,//设置为true,当数据长度超出列宽时将会自动截取 }); 省略号样式: <sty ...
- Solidity的三种合约间的调用方式 call、delegatecall 和 callcode
0x00 前言 Solidity(http://solidity.readthedocs.io/en/v0.4.24/) 是一种用与编写以太坊智能合约的高级语言,语法类似于 JavaScript. S ...
- Powershell 脚本判断制定路径下文件是否存在(来源于网络-转载)
$filelist=gc "file.txt" #获取要检查的文件列表 $csvs= new-object collections.arraylist #创建一个arraylist ...
- 通过github安装crawley出现的问题
http://www.cnblogs.com/hbwxcw/p/7086188.html
- 选择困难症的福音——团队Scrum冲刺阶段-Day 4
选择困难症的福音--团队Scrum冲刺阶段-Day 4 今日进展 编写提问部分 做了不同问题所对应的游戏选项,但关于游戏分类的界面还没有做完 增加功能 昨天在主界面增加"关于我们" ...