Thread.sleep和之前发生的关系是什么?

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)

CKi*_*ing 2

我试图了解 Thread.sleep 与 Happens-Before 有何关系

happens-before和之间没有关系。JLS明确指定没有任何同步语义Thread.sleepThread.sleep

零时间睡眠和屈服操作都不需要产生可观察到的效果。需要注意的是,Thread.sleepThread.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