Iva*_*sul 5 java multithreading
我写了一个简单的应用程序,它有主线程(生产者)和多个消费者线程.我想从主线程广播一条消息,所以所有的消费者线程都会收到它.但是,我有麻烦.我试图理解Thread.sleep如何与Happens-Before相关.这是我的一段代码:
import java.util.*;
public class PubSub {
public static void main(String[] args) {
List<Consumer> consumers = new ArrayList<>();
for (int i = 0; i < 3; i++ ) {
Consumer consumer = new Consumer(
"Consumer" + i
);
consumer.start();
consumers.add(consumer);
}
Scanner scanner = new Scanner(System.in);
while(true) {
String message = scanner.nextLine();
for (Consumer consumer: consumers) {
consumer.notify(message);
}
}
}
static class Consumer extends Thread {
private Queue<String> queue;
public Consumer(String name) {
super(name);
this.queue = new LinkedList<>();
}
@Override
public void run() {
while(true) {
if (!queue.isEmpty()) {
String message = queue.poll();
System.out.println(
getName() + ": Consuming message: " + message
);
}
}
}
public void notify(String message) {
queue.add(message);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我添加睡眠,消费者将开始接收消息:
@Override
public void run() {
while(true) {
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!queue.isEmpty()) {
String message = queue.poll();
System.out.println(
getName() + ": Consuming message: " + message
);
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于Thread.sleep是一个本机方法,我想了解它如何解决之前发生的事情.
我必须注意,之前修复的真正方法是生成volatile关键字:
private volatile Queue<String> queue;
Run Code Online (Sandbox Code Playgroud)
如果将同步添加到队列中,它还将解决问题:
@Override
public void run() {
synchronized (queue) {
while (true) {
if (!queue.isEmpty()) {
String message = queue.poll();
System.out.println(
getName() + ": Consuming message: " + message
);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我试图了解 Thread.sleep 与 Happens-Before 有何关系
happens-before和之间没有关系。JLS明确指定没有任何同步语义:Thread.sleepThread.sleep
零时间睡眠和屈服操作都不需要产生可观察到的效果。需要注意的是,Thread.sleep和Thread.yield都没有任何同步语义。特别是,编译器不必在调用 sleep 或yield 之前将缓存在寄存器中的写入刷新到共享内存,编译器也不必在调用 sleep 或yield 后重新加载缓存在寄存器中的值。例如,在以下(损坏的)代码片段中,假设 this.done 是非易失性布尔字段:
while (!this.done) Thread.sleep(1000);编译器可以自由地读取 this.done 字段一次,并在每次循环执行中重用缓存的值。这意味着循环永远不会终止,即使另一个线程更改了 this.done 的值
与此相关的是,如果在循环之后添加在运行时评估的语句,则消费者似乎开始消费消息(在我的机器上)while。例如,添加 会this.toString()导致消费者开始消费消息。
public void run() {
while(true) {
this.toString();
Run Code Online (Sandbox Code Playgroud)
这仍然不能解释该行为,但它进一步证实这不一定是突然看到对队列进行更新的Thread.sleep原因。Consumer