1

According to wikipedia: "The race conditions that cause spurious wakeups should be considered rare".

But when I run this code, it is showing me that spurious wakeup happens quite often.
Is this actually spurious wakeup or there's just a sneaky race condition in my code?

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();
        }
    }

}
Sergio
  • 438
  • 2
  • 6
  • `SpuriousMessage` definitely has a race (plain [race](http://en.wikipedia.org/wiki/Race_condition), coding mistake, nothing related to [spurious wakeups](http://programmers.stackexchange.com/a/187089/31260)): message sets and gets without checking whether it's empty – gnat Sep 29 '14 at 07:57
  • possible duplicate of [Do spurious wakeups actually happen?](http://stackoverflow.com/questions/1050592/do-spurious-wakeups-actually-happen) – gnat Oct 22 '14 at 03:28

1 Answers1

2

When a spurious wakeup happens, wait() exits although notify/notifyAll has not been called. In your case you call notifyAll from the producer so it is normal that wait exits...

To observe a spurious wakeup, you would need to run your Consumers only. If they print the "spurious wakeup" message then that will be a real spurious wakeup because it won't be caused by notify/All any more. However it may never happen.

See also: Do spurious wakeups actually happen?.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783