java线程没wait前调用notify,深入Java线程 之 wait和notify
wait和notify
多线程是需要同步协作,比如QuartzSchedulerThread,作为Quartz的任务调度线程,如果设置该线程暂停,那么这个线程就必须wait, 等外界通知继续的时候,再执行任务。
// check if we're supposed to pause...
synchronized (sigLock) {
while (paused && !halted.get()) {
try {
// wait until togglePause(false) is called...
//使当前线程暂停1秒
sigLock.wait(1000L);
} catch (InterruptedException ignore) {
}
}
if (halted.get()) {
break;
}
}
如果要调用对象的wait方法,就必须先获得这个对象的锁–>synchronized (sigLock)。
当线程执行了wait后,就释放了锁,这样当线程在wait期间,QuartzScheduler就有机会获得锁。然后就可以执行唤醒操作notify。
如果多个线程都在wait sigLock, 当调用 notify的时候只能唤醒其中一个线程(由JVM决定),调用notifyAll则能唤醒全部线程。
从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。
相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。
QuartzScheduler执行start方法,会调用schedThread.togglePause(false); 取消QuartzSchedulerThread的暂停,初始化的时候QuartzSchedulerThread的pause是true
。
void togglePause(boolean pause) {
synchronized (sigLock) {
paused = pause;
if (paused) {
signalSchedulingChange(0);
} else {
sigLock.notifyAll();
}
}
}
sleep 和 wait 的区别
Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。
经典的ABC打印问题
建立三个线程,A线程打印10次A,B线程打印10次B, C线程打印10次C,要求线程同时运行,交替打印10次ABC。
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
new Thread(pb).start();
new Thread(pc).start(); }
}
A—>B—>C—>A
要想控制线程执行的顺序,就需要使用锁机制,每个线程都持有自己的锁,并且在自己上一个线程的锁释放的时候才能执行。
比如B执行的条件:
1. A 锁释放
2. 获得自己的B 锁
A执行的条件:
1. C锁释放
2. 获得自己的A 锁
执行流程:
1. 获得前一个线程的锁 synchronized (prev)
2. 获得自己的锁,执行方法,释放自己的锁,通知下一个线程 synchronized (self) { self.notify(); }
3. 释放前一个线程的锁,继续监听锁,prev.wait();
总结:
wait就是监听锁对象,将当前线程放入锁的等待队列中。
notify就是通知监听线程,告诉监听该锁的线程,你们可以获得锁了。
notify只会让其中一个监听线程执行,由JVM决定。notifyAll会通知所有的监听线程。
那么问题来了!
如果A 执行了System.out.print(name);方法后,CPU又切换到了主线程。启动了B 和C 线程,
B 获取不到A锁,继续等待。C 获得了B锁,然后准备获得自己的C锁,这时A居然执行完成了,释放了C锁,所以C就可以执行方法了。 然后就是落后的B,先获得A锁,再获得B锁,执行。
就成了ACBACB…..
模拟代码:
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
try{
//先睡会,CPU你去找其他线程
Thread.sleep(1);
}
catch (InterruptedException e){
e.printStackTrace();
}
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
解决:
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--;
try{
Thread.sleep(1);
}
catch (InterruptedException e){
e.printStackTrace();
}
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);
new Thread(pa).start();
//使主线程睡眠,这样CPU就不会在A执行结束前去执行B或者C,给A足够的时间完成调度。
Thread.sleep(10);
//主线程睡眠之间到了时候,恢复CPU调度,启动线程B
new Thread(pb).start();
Thread.sleep(10);
new Thread(pc).start();
Thread.sleep(10);
}
}
synchronized的4种用法
1.方法声明
public synchronized void synMethod() {
//方法体
}
一次只能一个线程进入该方法。
2.对某一代码块使用
public int synMethod(int a1){
synchronized(a1) {
//一次只能有一个线程进入
}
}
文章来自
java线程没wait前调用notify,深入Java线程 之 wait和notify相关推荐
- java rtmp_搭建rtmp直播流服务之2:使用java实现ffmpeg命令接口化调用(用java执行ffmpeg命令)...
一.环境搭建 1.安装ffmpeg 下载对应系统的ffmpeg安装包,个人采用windows平台进行开发,所以安装了windows版本(各平台ffmpeg命令都是一样的,无须纠结) 2.ffmpeg的 ...
- idea 新建的java项目没发run_IntelliJ IDEA创建普通的Java 项目及创建 Java 文件并运行的教程...
最近突然看到这篇几年前随手记录的文章,居然浏览量那么高.看来很多小伙伴也开始从 Eclipse 转到 IDEA,这里为了让大家更好的掌握 IDEA 的使用,我建议大家可以看看下面这个 IDEA 教程. ...
- java多线程实战指南_学习笔记《Java多线程编程实战指南》二
2.1线程属性 属性 属性类型及用途 只读属性 注意事项 编号(id) long型,标识不同线程 是 不适合用作唯一标识 名称(name) String型,区分不同线程 否 设置名称有助于代码调试和问 ...
- 线程中这么调用类_这些线程知识总结是真的到位!java开发两年的我看的目瞪口呆
前言 什么是线程:程序中负责执行的那个东东就叫做线程(执行路线,进程内部的执行序列),或着说是进程的子任务. Java中实现多线程有几种方法 继承Thread类: 实现Runnable接口: 实现Ca ...
- 线程类中如何调用service_你真的了解Java多线程吗?
一.程序.进程和线程.单核CPU和多核CPU.并行与并发 什么是程序? 程序是为完成特定任务.用某种语言编写的一组指令的集合.即指一段静态的代码. 什么是进程? 进程是指程序的一次执行过程,或是正在运 ...
- Java线程面试的前50个问题,面向初学者和经验丰富的程序员
您可以参加任何Java面试,无论是大四还是中级,经验或新来的人,一定会看到线程,并发和多线程中的几个问题. 实际上,这种内置的并发支持是Java编程语言的最强优势之一,并帮助它在企业界和程序员中同 ...
- 线程组多次调用_详细分析 Java 中启动线程的正确和错误方式
start 方法和 run 方法的比较 代码演示: /** * * start() 和 run() 的比较 * * * @author 踏雪彡寻梅 * @version 1.0 * @da ...
- 【Java 线程的深入研究3】最简单实例说明wait、notify、notifyAll的使用方法
wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...
- Java 编程问题:十、并发-线程池、可调用对象和同步器
原文:Java Coding Problems 协议:CC BY-NC-SA 4.0 贡献者:飞龙 本文来自[ApacheCN Java 译文集],自豪地采用谷歌翻译. 本章包括涉及 Java 并发的 ...
最新文章
- python flask表单语法_Flask框架如何使用表单
- 教你打造 Android 中的 IOC 框架
- VTK:可视化之CurvedReformation
- nginx 禁止通过IP,未绑定域名访问服务器
- 【学神】1-4 用户及用户组管理
- 透视宝移动端对Unity手机游戏引擎监控实现
- 工具使用-----Jmeter教程 简单的压力测试
- c语言for嵌套循环语句,关于for嵌套循环语句的疑问
- 最新!全球学术排名出炉:21 所中国大学位居世界 100 强
- 《Android群英传》— Android 书籍
- BZOJ 1106: [POI2007]立方体大作战tet 树状数组 + 贪心
- android image 位移动画_Android共享元素转场动画实现
- 图像处理(一)图像灰度化的三种方式
- linux下geos如何编译,geos库交叉编译生成ARM平台库
- python竖线_Matplotlib示例.43 横线与竖线
- 元数据是什么?如何管理元数据?
- 设置input框只能输入数字或者只能输入英文
- 2022年金属非金属矿井通风上岗证题库及模拟考试
- 搜索引擎:高级搜索技巧(初)
- 12V铅酸电池充放电保护板