Java多线程 -- wait() 和 notify() 使用入门
在前面讲解synchronize的文章中,有提到wait和notify,大概描述了它的使用,这里我将根据官方api详细的教你如何使用。
所属对象
wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态。
文档分析
我们找到Object类,下载它的文档,翻译每个方法的注释。
总结如下:
wait() 和 notify() 必须由对象持有者去调用,有三种方式:
1️⃣执行该对象的synchronized实例方法
2️⃣执行synchronized代码块
3️⃣执行该类的synchronized静态方法当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。
在while循环里用wait操作性能更好(比if判断)
调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。
notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)
notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)
如果是synchronized声明的方法,wait()操作后会施放synchronized锁,相反notify()触发后会重拿起synchronized锁。
如果当前线程不是当前对象所持有,则会报异常IllegalMonitorStateException
实例
1. 通过调用对象的wait和notify实现
/** 调用对象的 wait 和 notify 实例
* Created by Fant.J.
*/
public class Demo {
private boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("修改flag线程执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.setFlag(true);
notify();
System.out.println("修改flag并释放锁成功");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (demo.isFlag() != true){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("flag为true时线程执行");
}
}).start();
}
}
修改flag线程执行
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.thread.waitNotify.Demo$2.run(Demo.java:41)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.thread.waitNotify.Demo$1.run(Demo.java:31)
at java.lang.Thread.run(Thread.java:748)
从运行结果可以看出,它报错IllegalMonitorStateException,我们上面有给出报该异常的原因,是因为没有没有获取到对象的监视器控制权,我们new了两个线程,一个调用了wait 一个调用了notify,jvm认为wait是一个线程下的wait,notify是另一个线程下的notify,事实上,我们想实现的是针对Demo对象的锁的wait和notify,所以,我们需要调用Demo对象的wait和notify方法。
修改后的代码:
/** 调用对象的 wait 和 notify 实例
* Created by Fant.J.
*/
public class Demo {
private boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (demo) {
System.out.println("修改flag线程执行");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo.setFlag(true);
demo.notify();
System.out.println("修改flag并释放锁成功");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (demo) {
while (demo.isFlag() != true) {
try {
demo.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("flag为true时线程执行");
}
}
}).start();
}
}
修改flag线程执行
修改flag并释放锁成功
flag为true时线程执行
修改了两处,一处是加了synchronized代码块,一处是添加了wait和notify的调用对象。
2. 通过synchronized修饰方法来实现
package com.thread.waitNotify_1;
/** 通过synchronized方法实现 wait notify
* Created by Fant.J.
*/
public class Demo2 {
private volatile boolean flag = false;
public synchronized boolean getFlag() {
System.out.println(Thread.currentThread().getName()+"开始执行...");
if (this.flag != true){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"执行结束...");
return flag;
}
public synchronized void setFlag(boolean flag) {
this.flag = flag;
notify();
}
public static void main(String[] args) {
Demo2 demo2 = new Demo2();
Runnable target1 = new Runnable() {
@Override
public void run() {
demo2.getFlag();
}
};
Runnable target2 = new Runnable() {
@Override
public void run() {
demo2.setFlag(true);
}
};
new Thread(target1).start();
new Thread(target1).start();
new Thread(target1).start();
new Thread(target1).start();
}
}
Thread-0开始执行...
Thread-1开始执行...
Thread-2开始执行...
Thread-3开始执行...
为什么四个线程都执行了呢?synchronized不是锁定线程了吗?我在上面8点中也有说明,wait()操作后,会暂时释放synchronized的同步锁,等notify()触发后,又会重拾起该锁,保证线程同步。
然后我们条用target2来释放一个线程:
new Thread(target1).start();
new Thread(target1).start();
new Thread(target1).start();
new Thread(target1).start();
new Thread(target2).start();
Thread-0开始执行...
Thread-1开始执行...
Thread-2开始执行...
Thread-3开始执行...
Thread-0执行结束...
可以看到只释放了一个线程,并且是第一个线程,如果有优先级,他也是释放第一个线程。
如果把notify改成notifyAll。
Thread-0开始执行...
Thread-2开始执行...
Thread-1开始执行...
Thread-3开始执行...
Thread-3执行结束...
Thread-1执行结束...
Thread-2执行结束...
Thread-0执行结束...
如何证明,每次notify后会拿到synchronized锁呢,我在执行notify后添加一些时间戳捕获帮助我们查看
public synchronized void setFlag(boolean flag) {
this.flag = flag;
// notify();
notifyAll();
System.out.println("测试notify触发后会不会等2s"+System.currentTimeMillis());
try {
Thread.sleep(2000);
System.out.println("测试notify触发后会不会等2s"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Thread-0开始执行...
Thread-1开始执行...
Thread-2开始执行...
Thread-3开始执行...
测试notify触发后会不会等2s1529817196847
测试notify触发后会不会等2s1529817198847
Thread-3执行结束...
Thread-2执行结束...
Thread-1执行结束...
Thread-0执行结束...
可以看到的确是notify重拾了synchronized的同步锁,执行完该方法后才会释放锁。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
六月安恒月赛——记录
有段时间没有练习了,趁着周末做一下安恒月赛,这次的月赛题目大部分比较简单。 PWN和CRYPTO比较生疏就没做。 0x01 WEB——ezupload 题目是最常规的上传绕过,这里可以直接修改Content-Type和添加图片文件头来绕过检测。 另外,上传的文件后缀会被修改,如上传:eval.php,最后会成为eval.gif,这里可以通过修改文件名为eval.php.php来绕过,如图: snipaste20180624_102349.png 上传后直接命令执行拿到flag: snipaste20180624_102500.png snipaste20180624_102516.png 0x02 WEB——Mynote 这道题相对也比较简单一些 snipaste20180624_102804.png 注册一个账号后,进入功能页面。可以上传文件、提交note等 snipaste20180624_102910.png 上传可以修改type绕过,但是没有给出上传后的地址。 这里的笔记功能还存在XSS问题,但是不能打谁的Cookie,此题貌似XSS没什么用。 snipaste20180624...
-
下一篇
Python3 与 C# 面向对象之~继承与多态
代码裤子:https://github.com/lotapp/BaseCode 在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master在线预览:http://github.lesschina.com/python/base/oop/2.继承与多态.html 2.继承 ¶ 2.1.单继承 ¶ 在OOP中,当我们定义一个Class的时候,可以从某个现有的Class继承 新的Class称为子类,而被继承的class称为 基类 或者 父类 Python的继承格式 ==> xxx(base_class) 小明兴高采烈的听着老师开新课,不一会就看见了一个演示Demo: In[1]: class Animal(object): def eat(self): print("动物会吃") class Cat(Animal): # 注意一下Python的继承格式 pass class Dog(Animal): pass def main(): cat = Cat() dog = Dog() cat.eat() dog.eat() if __na...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- MySQL数据库在高并发下的优化方案
- Dcoker安装(在线仓库),最新的服务器搭配容器使用
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2配置默认Tomcat设置,开启更多高级功能