线程:Java中wait、notify、notifyAll使用详解
基础知识
首先我们需要知道,这几个都是Object对象的方法。换言之,Java中所有的对象都有这些方法。
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait() throws InterruptedException {
wait(0);
}
其中notify()、notifyAll()、wait(long timeout)是本地方法
其次我们需要知道这几个方法主要是用来个线程之间通信的。那可能就有人会问,既然是用来线程之间通信的,那为什么这几个方法不是在线程类Thread上呢?对于这个问题,我们先来看下这几个方法的具体使用方式再来回答这个问题。
用法
Java中规定,在调用者三个方法时,当前线程必须获得对象锁。因此就得配合synchronized关键字来使用
//使用模式,不代表可运行代码
synchronized(object) {
while(contidion) {
object.wait();
}
//object.notify();
//object.notifyAll();
}
或者
public synchronized void methodName() {
while(contidion) {
object.wait();
}
//object.notify();
//object.notifyAll();
}
在synchronized拿到对象锁之后,synchronized代码块或者方法中,必定是会持有对象锁的,因此就可以使用wait()或者notify()。
通过上述使用方法,我们也能很好理解为什么这几个方法是在Object上而不是在Thread上。因为每个对象都可以作为synchronized锁的对象,因此wait、notify等必须和对象关联才能配合synchronized使用。
作用
| 方法 | 作用 |
|---|---|
| wait | 线程自动释放占有的对象锁,并等待notify。 |
| notify | 随机唤醒一个正在wait当前对象的线程,并让被唤醒的线程拿到对象锁 |
| notifyAll | 唤醒所有正在wait当前对象的线程,但是被唤醒的线程会再次去竞争对象锁。因为一次只有一个线程能拿到锁,所有其他没有拿到锁的线程会被阻塞。推荐使用。 |
实际案例
接下来我们就使用wait()、notify()来实现一个生产者、消费者模式。这个也是面试过程中可能会被问到的地方。至于什么是生产者消费者模式,不明白的同学请自行百度。
首先是一些基础的代码
private static Boolean run = true;//控制是否生产和消费
private static final Integer MAX_CAPACITY = 5;//缓冲区最大数量
private static final LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();//缓冲队列
生产者代码
/**
* 生产者
*/
class Producter extends Thread {
@Override
public void run() {
while (run) {
synchronized (queue) {
while (queue.size() >= MAX_CAPACITY * 2) {
try {
System.out.println("缓冲队列已满,等待消费");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
String string = UUID.randomUUID().toString();
queue.put(string);
System.out.println("生产:" + string);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.notifyAll();//通知生产者和消费者
}
}
}
}
消费者代码
/**
* 消费者
*/
class Consumer extends Thread {
@Override
public void run() {
while (run) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
System.out.println("队列为空,等待生产");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
System.out.println("消费:" + queue.take());
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.notifyAll();//通知生产者和消费者
}
}
}
}
代码说明
1、生产者和消费者都继承了线程Thread,因为wait、notify本身就是线程间通信使用
2、生产者和消费者都有两层while,外层的while是用来判断是否运行生产者和消费者。内存的while用来判断队列queue是否已满或者为空,如果满足条件,则使得当前线程变成等待状态(等待notify)。
3、内层的条件判断为什么用while不用if,原因是当线程被wait之后,会释放对象锁。当等待的线程被notify之后,必须再次尝试去获取对象锁,如果没有获取到对象锁,那还必须等待,直到拿到对象锁之后才能向后执行。
4、当生产者生产了一个数据或者消费者消费了一个数据之后,使用notifyAll()方法来通知所有等待当前对象锁的线程,但是一次只会有一个等待的线程能拿到锁。
5、我们使用queue作为锁的对象在不同线程之间进行通信
代码运行结果
队列为空,等待生产
生产:e422484e-8eb3-4a91-8a7c-97e21d7ef498
生产:7894b802-2529-4798-ba98-9f0657f39240
生产:848f6759-a427-4a94-89dc-3f484daaa467
生产:f711d3dc-972c-4c44-8640-faffe376d354
生产:38a08e62-d774-4ed5-8b51-f1ad534c246f
消费:e422484e-8eb3-4a91-8a7c-97e21d7ef498
消费:7894b802-2529-4798-ba98-9f0657f39240
消费:848f6759-a427-4a94-89dc-3f484daaa467
消费:f711d3dc-972c-4c44-8640-faffe376d354
消费:38a08e62-d774-4ed5-8b51-f1ad534c246f
队列为空,等待生产
生产:9fae26f3-0b6e-4fbd-9620-040667efe0af
生产:95bb1d88-e08a-4f70-a270-f75760994184
消费:9fae26f3-0b6e-4fbd-9620-040667efe0af
消费:95bb1d88-e08a-4f70-a270-f75760994184
队列为空,等待生产
生产:13d304bc-dff3-44a4-9527-2e0facd884e7
生产:2693e069-bae1-4beb-adcd-a3c3bf5d232b
消费:13d304bc-dff3-44a4-9527-2e0facd884e7
消费:2693e069-bae1-4beb-adcd-a3c3bf5d232b
由结果也可以看出来,生产和消费是无规则的,因为虽然notifyAll()通知了所有的等待线程,但是不确定那个线程中能拿到对象锁。但是也有一个很明显的问题,因为同时只有一个线程能拿到对象锁,生产者和消费者不可能同时运行。
线程:Java中wait、notify、notifyAll使用详解的更多相关文章
- Java 中的异常和处理详解
Java 中的异常和处理详解 原文出处: 代码钢琴家 简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误 ...
- Java中的多线程技术全面详解
本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...
- 高并发之wait notify notifyAll原理详解
public class WaitTest { public void testWait(){ System.out.println("Start-----"); try { wa ...
- Java中的IO流系统详解(转载)
摘要: Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java ...
- java中vector与hashtable操作详解
众所周知,java中vector与hashtable是线程安全的,主要是java对两者的操作都加上了synchronized,也就是上锁了.因此 在vector与hashtable的操作是不会出现问题 ...
- Java中的IO流系统详解
Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 U ...
- java中内存结构及堆栈详解
一. java内存结构 1. Heap(堆):实例分配的地方,通过-Xms与-Xmx来设置 2. MethodArea(方法区域):类的信息及静态变量. 对应是Permanet Generation, ...
- JAVA中IO和NIO的详解分析,内容来自网络和自己总结
用一个例子来阐释: 一辆客车上有10个乘客,他们的目的地各不相同,当没有售票员的时候,司机就需要不断的询问每一站是否有乘客需要下车,需要则停下,不需要则继续开车,这种就是阻塞的方式. 当有售票员的时候 ...
- Java中的异常和处理详解
简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?. ...
- java中的final和volatile详解
相比synchronized,final和volatile也是经常使用的关键字,下面聊一聊这两个关键字的使用和实现 1.使用 final使用: 修饰类表示该类为终态类,无法被继承 修饰方法表示该方法无 ...
随机推荐
- vm-install 模版创建虚拟机
主要用到的信息有:模版id和存储id 通过存储名字 # xe vm-install template=[template_uuid] new-name-label="name" s ...
- webdriver 属于selenium 体系中设计出来操作浏览器的一套API
1.元素的定位 1.id属性定位 实例: find_element_by_id("kw") 2.name属性定位,同id一样是属性值 实例: find_element_ ...
- iview 多选下拉列表选项回显问题
如,简单的多选Select, <Select v-model="model" filterable clearable transfer multiple > < ...
- angular8中 如何引入两个echarts版本
下载echarts2和echarts4的源码,然后在index.html中 1.先引入echarts4版本 2.然后引入echarts2版本 并且将echarts2源码中所有echarts 替换为ec ...
- 【Iterm2】如何解决iterm2窗口自动隐藏的问题
一.问题描述 当我们使用Iterm2的Hotkey Windom功能时,通过快捷键唤起Iterm2窗口后,然后鼠标在iterm2窗口之外触发点击操作就会让 iterm2窗口自动隐藏.. 这样有时候会觉 ...
- Nginx的特性功能-反向代理、负载均衡、缓存、动静分离、平滑升级
反向代理 nginx配置文件 events { } 事件驱动 httpd { } 关于httpd相关的配置 server { } 定义虚拟主机 location { } ...
- 打造好用的C++ IDE环境
https://www.jianshu.com/p/1aa989808e15 这哥们说的也是极好,也可以这部分直接看他的示例! mingw-w64应该可以算是mingw的改进版本吧,mingw系列编译 ...
- Java动态代理--CGLib实现
上一篇说了通过JDK实现实现了动态代理,但是用JDK实现代理,要求所代理的类必须实现一个接口.如果我们类不实现接口,这就在无法使用JDK提供的Proxy来实现动态代理,这个时候我们应该怎么办呢?我们找 ...
- Log4j 日志输出学习(Eclipse)
学习网址1:http://www.cnblogs.com/licheng/archive/2008/08/23/1274566.html 一.快速入手 1.官网下载log4j压缩包,本地加压 2.Ec ...
- 分享一个简单易用的软件定时器模块(MultiTimer)——基于keil+stm32f103zet+hal库(裸机实现)
公众号上看到一个比较好的一个github项目:https://github.com/0x1abin/MultiTimer 今天看了看,简单的,就移植了- 且看文档的说明, ============== ...