Spurious wakeups, wait() and notifyAll()

2020-06-24 01:42发布

I have N threads that do these things in loop: increase shared variable, checks if shared variable value is N (if so put value to queue) and do wait(). I have one more threat that checks the q variable. If queue is true, it does notifyAll(). After that N threads should wake up, and do another run of loop. But it seems that some threads are waking up without notification. I've read about spurious wakeups, but I don't know what condition should I put to while() to check if it was spurious wake up.

Bellow is example of my code (not the same, but very similar in meaning):

Shared class is shared between all threads:

class Shared {
    volatile int v = 0;
}

I create 10 threads and run them.

class NThreads implements Runnable {
    private Shared shared;
    private QThread q;
    static int N = 0; /* N - number of threads */

    public NThreads(QThread q, Shared shared) {
        this.q = q;
        this.shared = shared;
        ++N;
    }

    @Override
    public void run() {
        for (int i=0;i<1048575;++i) {
            doSomeCalculations();
            loop();
        }
    }

    private void loop() {
        synchronized (shared) {
            if (++shared.v == N) {
                shared.v = 0;
                synchronized (Q) {
                    q.q = true;
                }
                shared.wait();
            } else {
                shared.wait();
            }
        }
    }
}

There is only one QThread that waits for modification of q and then invoke notifyAll();

class QThread implements Runnable {
    private Shared shared;
    volatile boolean q = false;

    public QThread(Shared shared) {
        this.shared = shared;
    }

    @Override
    public void run() {
        for (;;) {
            if (q) {
                synchronized (this) {
                    q = false;
                }
                synchronized (shared) {
                    shared.notifyAll();
                }
            }
            doSomethingElse();
            // or even: Thread.yield();
        }
    }
}

Main class that runs everything:

class Main {
    public static void main(String[] args) {
        Shared shared = new Shared();
        QThread qt = new QThread(shared);
        NThread[] nt = new NThread[10];
        for (int i=0; i<nt.length; ++i) {
            nt[i] = new NThread(qt, shared);
        }
        Thread[] threads = new Thread[nt.length+1];
        threads[0] = new Thread(qt);
        for (int i=0; i<nt.length; ++i) {
            threads[1+i] = new Thread(nt[i]);
        }

        for (int i=0; i<threads.length; ++i) {
            threads[i].start();
        }

        for (int i=0; i<threads.length; ++i) {
            threads[i].join();
        }
    }
}

Sometimes there are threads (from NThreads) that finishes their loops, but other threads no. I think that is because of spurious wakeups. How to write condition to take cake of spurious wakeups and be sure that all threads can continue their work?

Maybe there is useful class in java.util.concurrent? But I didn't see anything interesting... :(

1条回答
神经病院院长
2楼-- · 2020-06-24 02:17

The JavaDocs for Object.wait() discuss the spurious wakeup possibility. It also mentions that you should use the wait() in a loop that checks an external condition before leaving the wait loop.

You'll need to restructure the loop() method a bit to accomplish this

private void loop() {
    synchronized (shared) {
        shared.v++;
        while(shared.v < N) {
            shared.wait();
        }

        shared.v = 0;
        synchronized (Q) {
            q.q = true;
        } 
    }

In the end, you should probably find a concurrency library that has this built in since it's easy to get these types of things wrong. I'd look at Google's Guava, java.util.concurrency or one of the many Apache Commons libraries as a starting point.

查看更多
登录 后发表回答