java多线程wait()方法必须放在while循环里面的原因探析
1、写一个包子生产消费案例:一次生产或消费一个包子,有包子就消费,没有就生产。(部分代码参考传智播客刘意2015Java基础视频讲义)
1.1 写一个Baozi.class,包含main()方法,用来测试
package com.oy.demo3; /*
* 包子生产消费案例:一次生产或消费一个包子,有包子就消费,没有就生产。
*/
public class Baozi {
// 默认是flag,表示没有包子,需要生产线程来生产包子;如果是true,说明有包子,需要消费端来消费包子。
public boolean flag; // 计数,当前在生产或消费第n个包子
public int count = 0; public static void main(String[] args) {
// 创建共享对象
Baozi s = new Baozi(); // 在外界把共享对象创建出来,通过构造方法传递给其他的类。这样st、gt1、gt2就共享s对象。
SetThread st = new SetThread(s);
GetThread gt1 = new GetThread(s);
GetThread gt2 = new GetThread(s); // 线程类
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt1);
Thread t3 = new Thread(gt2); // 启动线程
t1.start();
t2.start();
t3.start();
}
}
1.2 生产包子的线程类 SetThread.class
package com.oy.demo3; public class SetThread implements Runnable {
private Baozi s; public SetThread(Baozi s) {
this.s = s;
} @Override
public void run() {
while (true) {
synchronized (s) {
// 判断有没有
if (s.flag) { // 生产端,有就等待
try {
System.out.println("生产端:等待。。。");
s.wait(); // 等待,并且立即释放锁。将来醒过来的时候,是从这里醒过来的
System.out.println("生产端:醒过来了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 开始生产
s.count++;
System.out.println("生产第" + s.count + "包子。。。"); // 生产完后,修改标记为true
s.flag = true;
// 唤醒线程
s.notifyAll();
System.out.println("==========开始抢CPU的执行权==========");
}
}
}
}
1.3 消费包子的线程类 GetThread.class
package com.oy.demo3; public class GetThread implements Runnable {
private Baozi s; public GetThread(Baozi s) {
this.s = s;
} @Override
public void run() {
while (true) {
synchronized (s) {
while (!s.flag) { // 消费端,没有就等待
try {
System.out.println(Thread.currentThread().getName() + "消费端:等待。。。");
s.wait(); // 等待,并且立即释放锁。将来醒过来的时候,是从这里醒过来的
System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 开始消费
System.out.println(Thread.currentThread().getName() + "消费第" + s.count + "个包子"); // 消费完了,修改标记为false
s.flag = false;
// 唤醒线程
s.notifyAll();
System.out.println("==========开始抢CPU的执行权==========");
}
}
}
}
2、测试结果(只选择了控制台打印的部分结果):
==========开始抢CPU的执行权==========
生产端:等待。。。
Thread-2消费端:醒过来了。。。
Thread-2消费第6806个包子 // Thread-2消费完了第6806个包子,然后唤醒等待的线程;
==========开始抢CPU的执行权========== // 然后3个线程开始抢CPU的执行权
Thread-2消费端:等待。。。 // 消费端线程Thread-2抢到了,但是此时没有包子了,所以等待
Thread-1消费端:醒过来了。。。 // 然后,消费端线程Thread-1抢到了执行权,在原来wait()方法的地方醒过来,
// 执行wait()方法后面的代码System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。")
Thread-1消费端:等待。。。 // 然后继续while循环判断,由于此时没有包子,所以等待;
// 如果把while改成if,就不会判断是否有包子,直接执行后面的代码消费包子,
// 此时并没有包子了,这就产生了错误(同一个包子被消费了两次)。
生产端:醒过来了。。。
生产第6807包子。。。
==========开始抢CPU的执行权==========
3、对测试结果的分析:
3.1 首先明确,生产端开启了一个线程,消费端开启了两个线程。
3.2 Thread-2消费完了第6806个包子,然后唤醒等待的线程;然后3个线程开始抢CPU的执行权,消费端线程Thread-2抢到了,但是此时没有包子了,所以等待;
3.3 然后,消费端线程Thread-1抢到了执行权,在原来wait()方法的地方醒过来,执行wait()方法后面的代码System.out.println(Thread.currentThread().getName() + "消费端:醒过来了。。。"),然后继续while循环判断,由于此时没有包子,所以等待。如果把while改成if,就不会判断是否有包子,直接执行后面的代码消费包子,此时并没有包子了,这就产生了错误(同一个包子被消费了两次)。
3.4 综上所述:wait()方法套在while循环中,线程下次醒过来后会继续进行循环,判断条件是否满足,满足就重新等待。
3.5 由于生产端只开启了一个线程,所以将wait()方法套在if代码块中也是可以的,当然使用while也可以。
java多线程wait()方法必须放在while循环里面的原因探析的更多相关文章
- Java 多线程 sleep方法与wait方法的区别
sleep方法会使线程暂停执行一段时间,wait方法会阻塞线程,直到被唤醒或等待时间超时. 两者区别具体如下: 1 原理不同 sleep方法是Thread类的静态方法,使线程暂停执行一段时间,等到计时 ...
- Java多线程-run方法与start方法的区别
package com.interview; /** * java多线程的两种实现方式以及run.start方法的区别 * @author MEI.LIU * */ public class Thre ...
- Java多线程-join方法
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程.比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B. 具体例子看链接 ...
- Java——多线程之方法详解
Java多线程系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多 ...
- Java多线程优化方法及使用方式
一.多线程介绍 在编程中,我们不可逃避的会遇到多线程的编程问题,因为在大多数的业务系统中需要并发处理,如果是在并发的场景中,多线程就非常重要了.另外,我们在面试的时候,面试官通常也会问到我们关于多线程 ...
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- java多线程 join方法以及优先级方法
/*join:当A线程执行到了B线程的.join()方法时,A就会等待.等B线程都执行完,A才会执行. join可以用来临时加入线程执行. 1.线程使用join方法,主线程就停下,等它执行完,那么如果 ...
- 2017第45周一java多线程创建方法
1. 从JDK1.5开始,Java提供了3种方式来创建,启动多线程: Ø 继承Thread类来创建线程类,重写run()方法作为线程执行体. Ø 实现Runnable接口来创建线程类,重写run()方 ...
- Java 多线程 interrupt方法
interrupt 下面是interrupt方法的文档的一部分: * <p> If this thread is blocked in an invocation of the {@lin ...
随机推荐
- 【.Net】在windows server 2016 和Windows10这些server上安装.net fw3.5
一般就是打开server manager. 一直next到add feature 讲net3.5勾选 发现需要指定一个路径是什么 source\sxs之类的 下载microsoft-windows-n ...
- 17秋 SDN课程 第二次上机作业
1.控制器floodlight所示可视化图形拓扑的截图,及主机拓扑连通性检测截图 拓扑 连通性 2.利用字符界面下发流表,使得'h1'和'h2' ping 不通 流表截图 连通性 3.利用字符界面下发 ...
- 常用模块(subprocess/hashlib/configparser/logging/re)
一.subprocess(用来执行系统命令) import os cmd = r'dir D:xxx | findstr "py"' # res = subprocess.Pope ...
- React Native原生模块向JS传递数据的几种方式(Android)
一般情况可以分为三种方式: 1. 通过回调函数Callbacks的方式 2. 通过Promises的异步的方式 3. 通过发送事件的事件监听的方式. 参考文档:传送门
- HTML学习笔记CSS
类选择器和ID选择器的区别 1id只能用一回,类可以循环使用 2可以使用类选择器词列表方法为一个元素同时设置多个样式.我们可以为一个元素同时设多个样式,但只可以用类选择器的方法实现,ID选择器是不可以 ...
- java递归 处理权限管理菜单树或分类
1.数据库表设计 2.实体类设计 package com.ieou.capsule.dto.SystemPermissions; import java.util.List; /** * 功能菜单类 ...
- Spring boot @Scheduled(cron = "* * * * * *") cron表达式详解
//@Scheduled(cron = "0 0/15 * * * ?") //每15分钟触发一次 //@Scheduled(cron = "5/10 * * * * ? ...
- sklearn preprocessing (预处理)
预处理的几种方法:标准化.数据最大最小缩放处理.正则化.特征二值化和数据缺失值处理. 知识回顾: p-范数:先算绝对值的p次方,再求和,再开p次方. 数据标准化:尽量将数据转化为均值为0,方差为1的数 ...
- “ORA-06550: 第 1 行, 第 7 列”解决方法
将本机能正常运行的维修生产日志代码发布到公司内测环境里无法正常运行,报错如下: execute() - pls–QuartzJob.java–quartzjob 开始执行! java.sql.SQLE ...
- R语言 set.seed()函数
看了几个帖子,说得不是特别清楚,特重新描述如下: set.seed()函数是为了保证你随机生成的随机数前后一致,看效果 首先,不设置该种子函数. x=rnorm(10) plot(x)绘出的图如下: ...