- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章java wait()/notify() 实现生产者消费者模式详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
java中的多线程会涉及到线程间通信,常见的线程通信方式,例如共享变量、管道流等,这里我们要实现生产者消费者模式,也需要涉及到线程通信,不过这里我们用到了java中的wait()、notify()方法:
wait():进入临界区的线程在运行到一部分后,发现进行后面的任务所需的资源还没有准备充分,所以调用wait()方法,让线程阻塞,等待资源,同时释放临界区的锁,此时线程的状态也从RUNNABLE状态变为WAITING状态; 。
notify():准备资源的线程在准备好资源后,调用notify()方法通知需要使用资源的线程,同时释放临界区的锁,将临界区的锁交给使用资源的线程.
wait()、notify()这两个方法,都必须要在临界区中调用,即是在synchronized同步块中调用,不然会抛出IllegalMonitorStateException的异常.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package
threads;
import
java.util.List;
import
java.util.UUID;
public
class
Producer
extends
Thread{
private
List<String> storage;
//生产者仓库
public
Producer(List<String> storage) {
this
.storage = storage;
}
public
void
run(){
//生产者每隔1s生产1~100消息
long
oldTime = System.currentTimeMillis();
while
(
true
){
synchronized
(storage){
if
(System.currentTimeMillis() - oldTime >=
1000
) {
oldTime = System.currentTimeMillis();
int
size = (
int
)(Math.random()*
100
) +
1
;
for
(
int
i =
0
; i < size; i++) {
String msg = UUID.randomUUID().toString();
storage.add(msg);
}
System.out.println(
"线程"
+
this
.getName()+
"生产消息"
+size+
"条"
);
storage.notify();
}
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package
threads;
import
java.util.List;
public
class
Consumer
extends
Thread{
private
List<String> storage;
//仓库
public
Consumer(List<String> storage) {
this
.storage = storage;
}
public
void
run(){
while
(
true
){
synchronized
(storage){
//消费者去仓库拿消息的时候,如果发现仓库数据为空,则等待
if
(storage.isEmpty()) {
try
{
storage.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
int
size = storage.size();
for
(
int
i = size -
1
; i >=
0
; i--) {
storage.remove(i);
}
System.out.println(
"线程"
+
this
.getName()+
"成功消费"
+size+
"条消息"
);
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
threads;
import
java.util.ArrayList;
import
java.util.List;
public
class
Storage {
private
List<String> storage;
//生产者和消费者共享的仓库
public
Storage() {
storage =
new
ArrayList<String>();
}
public
List<String> getStorage() {
return
storage;
}
public
void
setStorage(List<String> storage) {
this
.storage = storage;
}
}
|
1
2
3
4
5
6
7
8
9
10
|
package
threads;
public
class
App {
public
static
void
main(String[] args) {
Storage storage =
new
Storage();
Producer producer =
new
Producer(storage.getStorage());
Consumer consumer =
new
Consumer(storage.getStorage());
producer.start();
consumer.start();
}
}
|
我们知道,java的wait/notify的通知机制可以用来实现线程间通信。wait表示线程的等待,调用该方法会导致线程阻塞,直至另一线程调用notify或notifyAll方法才可另其继续执行。经典的生产者、消费者模式即是使用wait/notify机制得以完成。在这篇文章中,我们将深入解析这一机制,了解其背后的原理.
在了解wait/notify机制前,先熟悉一下java线程的几个生命周期。分别为初始(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)、终止(TERMINATED)等状态(位于java.lang.Thread.State枚举类中).
以下是对这几个状态的简要说明,详细说明见该类注释.
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但未调用start()方法 |
RUNNABLE | 运行状态,调用start()方法后。在java线程中,将操作系统线程的就绪和运行统称运行状态 |
BLOCKED | 阻塞状态,线程等待进入synchronized代码块或方法中,等待获取锁 |
WAITING | 等待状态,线程可调用wait、join等操作使自己陷入等待状态,并等待其他线程做出特定操作(如notify或中断) |
TIMED_WAITING | 超时等待,线程调用sleep(timeout)、wait(timeout)等操作进入超时等待状态,超时后自行返回 |
TERMINATED | 终止状态,线程运行结束 |
对于以上线程间的状态及转化关系,我们需要知道 。
让我们先通过一个示例解析 。
wait()方法可以使线程进入等待状态,而notify()可以使等待的状态唤醒。这样的同步机制十分适合生产者、消费者模式:消费者消费某个资源,而生产者生产该资源。当该资源缺失时,消费者调用wait()方法进行自我阻塞,等待生产者的生产;生产者生产完毕后调用notify/notifyAll()唤醒消费者进行消费.
以下是代码示例,其中flag标志表示资源的有无.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
public
class
ThreadTest {
static
final
Object obj =
new
Object();
private
static
boolean
flag =
false
;
public
static
void
main(String[] args)
throws
Exception {
Thread consume =
new
Thread(
new
Consume(),
"Consume"
);
Thread produce =
new
Thread(
new
Produce(),
"Produce"
);
consume.start();
Thread.sleep(
1000
);
produce.start();
try
{
produce.join();
consume.join();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
// 生产者线程
static
class
Produce
implements
Runnable {
@Override
public
void
run() {
synchronized
(obj) {
System.out.println(
"进入生产者线程"
);
System.out.println(
"生产"
);
try
{
TimeUnit.MILLISECONDS.sleep(
2000
);
//模拟生产过程
flag =
true
;
obj.notify();
//通知消费者
TimeUnit.MILLISECONDS.sleep(
1000
);
//模拟其他耗时操作
System.out.println(
"退出生产者线程"
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者线程
static
class
Consume
implements
Runnable {
@Override
public
void
run() {
synchronized
(obj) {
System.out.println(
"进入消费者线程"
);
System.out.println(
"wait flag 1:"
+ flag);
while
(!flag) {
//判断条件是否满足,若不满足则等待
try
{
System.out.println(
"还没生产,进入等待"
);
obj.wait();
System.out.println(
"结束等待"
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(
"wait flag 2:"
+ flag);
System.out.println(
"消费"
);
System.out.println(
"退出消费者线程"
);
}
}
}
}
|
输出结果为:
进入消费者线程 。
wait flag 1:false 。
还没生产,进入等待 。
进入生产者线程 。
生产 。
退出生产者线程 。
结束等待 。
wait flag 2:true 。
消费 。
退出消费者线程 。
理解了输出结果的顺序,也就明白了wait/notify的基本用法。有以下几点需要知道:
这一节我们探讨wait/notify与线程状态之间的关系。深入了解线程的生命周期.
由前面线程的状态转化图可知,当调用wait()方法后,线程会进入WAITING(等待状态),后续被notify()后,并没有立即被执行,而是进入等待获取锁的阻塞队列.
对于每个对象来说,都有自己的等待队列和阻塞队列。以前面的生产者、消费者为例,我们拿obj对象作为对象锁,配合图示。内部流程如下 。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.
原文链接:https://blog.csdn.net/u014039577/article/details/52243116 。
最后此篇关于java wait()/notify() 实现生产者消费者模式详解的文章就讲到这里了,如果你想了解更多关于java wait()/notify() 实现生产者消费者模式详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在一次采访中遇到过这个问题。 线程中wait和wait on time有什么区别? 我知道 wait 方法 导致当前线程等待,直到另一个线程调用此对象的 notify() 方法或 notifyAll
我在这里得到了一个 java 代码片段,这让我想知道调用 wait() 和 this.wait() 之间的区别是什么。 假设您有一个类,该类具有获取资源的方法并且是同步的。通常,如果资源不可用,我会在
我知道如何使用 wait_event 在 Linux 内核队列中等待以及如何唤醒它们。 现在我需要弄清楚如何同时在多个队列中等待。我需要多路复用多个事件源,基本上以类似于 poll 或 select
c系统编程中wait(null)和wait(&status)有什么区别? 指针状态的内容是什么? 最佳答案 如果您调用 wait(NULL) ( wait(2) ),您只会等待任何子进程终止。使用 w
设想: 用户单击 View 上的按钮 这会调用 ViewModel 上的命令 DoProcessing 考虑到 View 和 ViewModel 的职责,Wait 光标是如何以及在哪里设置的? 为了清
我在使用 Selenium 的代码中看到了 FluentWait 和 WebDriverWait。 FluentWait 使用轮询技术,即它将在每个固定时间间隔轮询特定的 WebElement。我想知
我编写了以下代码,其中 start 方法应该等待,直到 stop 方法通知它。但是在执行过程中,尽管我已指定它等待,但启动方法下面的日志行会被打印。下图是我的start方法实现如下。 private
我有以下连接到 SignalR Hub 的代码 private static async Task StartListening() { try {
我对线程中的 wait() 方法如何工作感到很困惑。假设我写: public class test3 { public static void main(String args[]){
在使用 Java 线程原语构造线程安全有界队列时 - 这两种构造之间有什么区别 创建显式锁定对象。 使用列表作为锁并等待它。 示例 1 private final Object lock = new
故事: 在 Java selenium 语言绑定(bind)中有一个 FluentWait class ,这允许严格控制如何检查预期条件: Each FluentWait instance defin
wait-die 和 wound-wait 算法有什么区别? 这两种死锁预防技术似乎都在做同样的事情:回滚旧进程。 两者有什么区别? 请提供一个合适的例子来对比这两种算法。 最佳答案 Wait-Die
在 Java 线程转储中,您可以看到堆栈跟踪中提到的锁。 似乎有三种信息: 1: - locked (a java.io.BufferedInputStream) 2: - waiting to l
以下代码运行大约需要 20 秒。然而,取消注释 do! 后只用了不到一秒的时间。为什么会有这么大的差异? 更新:使用ag.Add时需要9秒。我已经更新了代码。 open FSharpx.Control
我在 ASP.NET WebForms 网站上有一个服务器端点击事件。在这种情况下,我调用一个方法,该方法又调用其异步合作伙伴方法,在调用中添加 .Wait()。 此方法然后向下几个级别(即,调用另一
有 3 种状态的线程处于 Activity 状态但既不运行也不可运行:- sleep 已阻止 正在等待 当线程执行 sleep() 方法时,它会在其参数指定的时间段(比如几毫秒)内从运行状态进入休眠状
考虑以下代码 public class ThreadTest1 { private static final long startTime = System.currentTimeMillis();
我有一个使用线程的 Java 应用程序,它使用多个 Lock 对象实例来同步对公共(public)资源的访问。 现在,作为性能测量的一部分,我想测量每个线程在每个锁中花费的时间。到目前为止,我已经尝试
我写了下面这段代码: let first_row = rows_stream.take(1).wait(); 并收到以下错误(当我真正想要访问该元素时): found struct `futures:
我使用了两个命令来等待设备启动:adb 等待设备和 adb 等待设备。两者似乎都在等待设备启动,我发现它们的行为没有任何区别。他们的行为有什么不同吗? 添加更多关于我所做的信息: 所以这就是我所做的,
我是一名优秀的程序员,十分优秀!