java - 这是一个虚假的唤醒还是只是一个时髦的(非虚假的)竞争条件?

标签 java multithreading synchronization

根据wikipedia :“导致虚假唤醒的竞争条件应该被认为是罕见的”

但是当我运行这段代码时,它向我表明虚假唤醒经常发生。
这实际上是虚假唤醒还是我的代码中存在偷偷摸摸的竞争条件?

import java.util.Random;

public class Main {

    public static void main(String[] args) throws Exception {
        // Message message = new SafeMessage();
        Message message = new SpuriousMessage();

        String[] producerNames = { "p01", "p02", "p03", "p04", "p05", "p06", "p07", "p08", "p09" };
        for (String producerName : producerNames) {
            Producer producer = new Producer(producerName, message);
            new Thread(producer).start();
        }

        String[] consumerNames = { "c-01", "c-02", "c-03", "c-04" };
        for (String consumerName : consumerNames) {
            Consumer consumer = new Consumer(consumerName, message);
            new Thread(consumer).start();
        }
    }

}

abstract class Message {

    protected String message;
    protected boolean empty = true;

    public abstract String getMessage() throws InterruptedException;

    public abstract void setMessage(String message) throws InterruptedException;

    protected static String avoidNull(String obj) {
        return obj != null ? obj : "Default message";
    }

}

class SpuriousMessage extends Message {

    @Override
    public synchronized String getMessage() throws InterruptedException {
        wait();
        empty = true;
        String temp = message;
        message = "---------------------------------------- Spurious wakeup";
        return temp;
    }

    @Override
    public synchronized void setMessage(String message) throws InterruptedException {
        this.message = avoidNull(message);
        this.empty = false;
        notifyAll();
    }

}

class SafeMessage extends Message {

    @Override
    public synchronized String getMessage() throws InterruptedException {
        while (empty) {
            wait();
        }
        empty = true;
        notifyAll();
        String temp = message;
        message = "---------------------------------------- Spurious wakeup";
        return temp;
    }

    @Override
    public synchronized void setMessage(String message) throws InterruptedException {
        while (!empty) {
            wait();
        }
        this.message = avoidNull(message);
        this.empty = false;
        notifyAll();
    }

}

class Producer implements Runnable {

    private static final Random RANDOM = new Random();
    private String producerName = "Default";
    private Message message;

    public Producer(String producerName, Message message) {
        this.producerName = producerName;
        this.message = message;
    }

    @Override
    public void run() {
        while (true) {
            try {
                message.setMessage(producerName + " :: " + randomMessage());
                rest(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static String randomMessage() {
        final String[] messageArray = { "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
                "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar",
                "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
                "Xray", "Yankee", "Zulu" };
        return messageArray[RANDOM.nextInt(messageArray.length)];
    }

    private void rest(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

class Consumer implements Runnable {

    private final long TIMEOUT = 5;

    private String consumerName = "Default";
    private Message message;

    public Consumer(String consumerName, Message message) {
        this.consumerName = consumerName;
        this.message = message;
    }

    @Override
    public void run() {
        while (true) {
            try {
                System.out.println(consumerName + " :: " + message.getMessage());
                rest(TIMEOUT);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void rest(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

最佳答案

当发生虚假唤醒时,wait() 会退出,尽管 notify/notifyAll 尚未被调用。在您的情况下,您从生产者调用 notifyAll ,因此 wait 退出是正常的...

要观察虚假唤醒,您只需运行您的消费者。如果他们打印“虚假唤醒”消息,那么这将是真正的虚假唤醒,因为它不再是由 notify/All 引起的。然而这可能永远不会发生。

另请参阅:Do spurious wakeups actually happen? .

关于java - 这是一个虚假的唤醒还是只是一个时髦的(非虚假的)竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26499850/

相关文章:

java - 如何编写一个程序将二进制数转换为十进制数而不使用 lang 类?

java - 在警报对话框中获取用户选择

c++ - 控制台菜单更新 OpenGL 窗口

进程同步

c++ - 来自不同线程的同步输出流

java - 在文件夹中创建文件

java - 使用 JDBC 执行查询的 executeUpdate 时出现空指针异常

ios - 在 iOS 的后台线程中上传

c# - 在这种情况下锁定是否保证多个修改在线程间是原子的?

java - 在 Android 手机之间同步文本