Question

I recently learned "Spurious wakeups" Any people say that this problem possible only for some types of Linux PC.

I use windows.

I wrote test for Spurious wakeups. I got result that it is possible. But I want to show this test for you. Maybe I made mistake somewhere.

my initial variant:

import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestSpuriousWakeups {
    static final int MAX_THREADS = 600;

    static final Object mutex = new Object();

    static final CountDownLatch allThreadsStarted =
            new CountDownLatch(MAX_THREADS);
    static final CountDownLatch allThreadsFinished =
            new CountDownLatch(1);

    static /*final*/ volatile AtomicInteger processedThreads = new AtomicInteger();
    static /*final*/ volatile AtomicInteger notifiedThreads = new AtomicInteger();

    final  int n  = 10;

    static volatile boolean continueCondition = true;

    static final Random sleepRandom = new Random();

    static class Worker extends Thread {
        public void run() {
            try {
                synchronized (mutex) {
                    allThreadsStarted.countDown();

                    mutex.wait();
                }

                continueCondition = true;
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                processedThreads.incrementAndGet();
            }
        }
    }

    static class Notifier extends Thread {
        public void run() {
            while (true) {

                if (processedThreads.get() == MAX_THREADS)
                    break;

                synchronized (mutex) {
                    doStuff();

                    mutex.notify();
                    continueCondition = false;
                    notifiedThreads.incrementAndGet();
                }
            }

            allThreadsFinished.countDown();
        }

        // just to emulate some activity
        void doStuff() {
            try { Thread.sleep(sleepRandom.nextInt(5)); }
            catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < MAX_THREADS; i++)
            new Worker().start();

        // wait for all workers to start execution
        allThreadsStarted.await();

        new Notifier().start();

        // wait for all workers and notifier to finish execution
        allThreadsFinished.await();

        System.out.println("Spurious wakeups count: "
                + (MAX_THREADS - notifiedThreads.get()));
    }
}

4 random execution:

Spurious wakeups count: -20
Spurious wakeups count: -5
Spurious wakeups count: 0
Spurious wakeups count: -407

So different values is wondering for me.

I added pair of rows to run method:

static class Notifier extends Thread {
        public void run() {
            while (true) {

                while (!continueCondition)  //added string
                    doStuff();              //added string            

                // all threads finished their execution
                if (processedThreads.get() == MAX_THREADS)
                    break;

                synchronized (mutex) {
                    doStuff();

                    mutex.notify();
                    continueCondition = false;
                    notifiedThreads.incrementAndGet();
                }
            }

            allThreadsFinished.countDown();
        }

after it I cannot get something another than

Spurious wakeups count: 0

Is it really Spurious wakeups or bug in my experiment ?

P.S.

I noticed that I see negatives numbers. Thus obviously it is experiment bug. But I don't understand cause.

Was it helpful?

Solution

Two things

  1. Spurious wake ups are real, even on Windows. This is documented in the WinAPI: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682052(v=vs.85).aspx
  2. You have a race condition in your test. So, I don't think it's quite accurate.

The race is between the exit of the synchronized block in your worker threads and when they reach processedThreads.incrementAndGet(). Notifier will spin during that time, notifying threads which may or may not have acquired the lock.

In other words

  1. It's possible for Notifier to spin twice (that is, notify() twice) before a worker thread can acquire the mutex.
  2. It's possible for Notifier to spin after the last thread has exited the synchronized block but not yet reached its finally block.

Your two added lines change the output because, by slowing down the Notifier, you're masking the race. (By giving Worker lots of time to enter the mutex.)

Hope that makes some sense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top