Thread.sleep和before-before之间有什么关系?
我编写了一个简单的应用程序,它具有主线程(生产者)和多个使用者线程.我想广播来自主线程的消息,因此所有使用者线程都将收到该消息. 但是,我有麻烦. 我试图了解 Thread.sleep 可能与
I've written a simple application, which has main thread(producer) and multiple consumer threads. I want to broadcast a message from main thread, so all the consumer threads will receive it. However, I have troubles. I'm trying to understand how Thread.sleep may be related to Happens-Before. Here's my piece of code:
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);
}
}
}
如果我添加睡眠,则消费者将开始接收消息:
If I add sleep, the consumer will start receiving messages:
@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
);
}
}
}
由于Thread.sleep是本机方法,所以我想了解它是如何解决事前发生的.
Since Thread.sleep is a native method, I want to understand how it resolves happens-before.
我必须注意,修复之前发生的真正方法是使 volatile 关键字:
I must note, that the true way of fixing happens-before is to make volatile keyword:
private volatile Queue<String> queue;
如果将同步添加到队列,它还将解决此问题:
If you add synchronization onto the queue, it will also fix the issue:
@Override
public void run() {
synchronized (queue) {
while (true) {
if (!queue.isEmpty()) {
String message = queue.poll();
System.out.println(
getName() + ": Consuming message: " + message
);
}
}
}
}
我正试图了解Thread.sleep可能与Happens-Before相关联
I'm trying to understand how Thread.sleep may be related to Happens-Before
之前发生和Thread.sleep
之间没有没有关系. JLS 明确指定Thread.sleep
没有任何同步符号:
There is no relationship between happens-before and Thread.sleep
. The JLS clearly specifies that Thread.sleep
does not have any synchronization symantics :
零时间睡眠或屈服操作都不需要产生可观察到的效果. 请务必注意, Thread.sleep 和 Thread.yield 都没有任何同步 语义.特别是,编译器不必将缓存在寄存器中的写入刷新到 在调用sleep或yield之前共享内存,编译器也不必重新加载缓存的值 在调用睡眠或屈服后的寄存器中.例如,在以下(不完整的)代码片段中, 假设this.done是非易失性布尔字段:
Neither a sleep for a period of zero time nor a yield operation need have observable effects. It is important to note that neither Thread.sleep nor Thread.yield have any synchronization semantics. In particular, the compiler does not have to flush writes cached in registers out to shared memory before a call to sleep or yield, nor does the compiler have to reload values cached in registers after a call to sleep or yield. For example, in the following (broken) code fragment, assume that this.done is a non-volatile boolean field:
while (!this.done)
Thread.sleep(1000);
while (!this.done)
Thread.sleep(1000);
编译器可以自由读取一次this.done字段,并在每次循环执行中重用缓存的值.这意味着即使另一个线程更改了this.done
The compiler is free to read the field this.done just once, and reuse the cached value in each execution of the loop. This would mean that the loop would never terminate, even if another thread changed the value of this.done
在相关说明中,如果消费者在while
循环之后立即添加了在运行时求值的语句,则消费者似乎开始使用(在我的机器上)消息.例如,添加this.toString()
会使使用者开始使用消息.
On a related note, the consumer seems to start consuming messages (on my machine) if one adds statements that are evaluated at runtime right after the while
loop. For example, the addition of this.toString()
causes the consumer to start consuming messages.
public void run() {
while(true) {
this.toString();
这仍然不能解释行为,但可以进一步确认Thread.sleep
不一定是Consumer
突然看到对队列所做的更新的原因.
This still does not explain the behavior but it further confirms that Thread.sleep
is not necessarily the reason behind the Consumer
suddenly seeing the updates made to the queue.