简体   繁体   中英

How to use wait() and notify() in Java?

As I understand, I am suppose to call wait() on the mutex, when I want the current thread to stop working until another thread calls notify() on the same mutex object. That doesn't seem to be working.

I'm trying to make a thread print 1-10. Then wait for another thread to print 11-20. And then the first thread would again print 21-30

Main.java

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Object mutex = 1;

        Thread child1 = new Thread(new Child1(mutex));
        Thread child2 = new Thread(new Child2(mutex));

        child1.start();
        child2.start();

    }

}

Child1.java

public class Child1 implements Runnable {
    Object mutex;

    public Child1(Object mutex){
        this.mutex = mutex;
    }

    public void run() {
        synchronized (mutex) {
            for(int c = 0; c < 10; c++){
                System.out.println(c+1);
            }

            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }


        for(int c = 20; c < 31; c++){
            System.out.println(c+1);
        }
    }
}

Child2.java

public class Child2 implements Runnable {
    Object mutex;

    public Child2(Object mutex) {
        this.mutex = mutex;
    }

    public void run() {
        synchronized (mutex) {
            for (int c = 11; c < 21; c++) {
                System.out.println(c);
            }
            notify();
        }

    }
}

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Exception in thread "Thread-0" 
18
19
20
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at task42.Child1.run(Child1.java:18)
    at java.lang.Thread.run(Thread.java:745)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at task42.Child2.run(Child2.java:15)
    at java.lang.Thread.run(Thread.java:745)

What am I missing?

You must add the mutex reference to wait() and notify() ; that is, change wait() to mutex.wait() and notify() to mutex.notify() .

Without this, you are calling to wait/notify on this ( method() is equivalent to this.method() )

Here is your code with the appropriate changes made:

Child1.java

public class Child1 implements Runnable {
    Object mutex;

    public Child1(Object mutex){
        this.mutex = mutex;
    }

    public void run() {
        synchronized (mutex) {
            for(int c = 0; c < 10; c++){
                System.out.println(c+1);
            }

            try {
                mutex.wait(); // Changed here
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }


        for(int c = 20; c < 31; c++){
            System.out.println(c+1);
        }
    }
}

Child2.java

public class Child2 implements Runnable {
    Object mutex;

    public Child2(Object mutex) {
        this.mutex = mutex;
    }

    public void run() {
        synchronized (mutex) {
            for (int c = 11; c < 21; c++) {
                System.out.println(c);
            }
            mutex.notify(); // Changed here
        }

    }
}

Your code fails in several ways.

First of all, there is no guarantee, that the first thread will also run first. (Especially on multi-cores, there is a high chance, that both run in parallel). So if the second thread enteres the synchronized block of Child2.run() first, it will call mutex.notify() even before the first thread is in wait state. As a result, the first thread will stay in mutex.wait() forever.

Second, wait() / notify() are not thought to be used as thread-handshake-mechanism directly. This could only work if you could guarantee that the first thread calls wait() before the second thread calls notify() . Usually, you can't.

Instead, wait() should be used to wait for a certain condition to become true. The condition is usually changed by another thread, who notifies the waiting thread by calling notifyAll() . So the handshake-mechanism is the condition, not wait / notify :

// 1st thread:
synchronized (lock) {
    while (!condition) {
        lock.wait();
    }
    // continue
}

// 2nd thread:
synchronized {
    condition = true;
    lock.notifyAll();
}

Any other usage-pattern of wait() / notify() or notifyAll() is wrong! It is also very important to always call wait() inside a loop as a thread might wake up on chance - even without notify() or notifyAll() .

Using wait()/notifyAll()

So in your case, you could use wait() and notifyAll() in combination with a stage-variable:

public class Mutex {
    static final Object lock = new Object();
    static int stage = 1;

    static void first() throws InterruptedException {
        synchronized (lock) {
            // we're already in stage 1
            for(int i = 0; i < 10; ++i) System.out.println(i);

            // enter stage 2
            stage = 2;
            lock.notifyAll();

            // wait for stage 3
            while (stage != 3) { lock.wait(); }

            // now we're in stage 3
            for(int i = 20; i < 30; ++i) System.out.println(i);
        }
    }

    static void second() throws InterruptedException {
        synchronized (lock) {
            // wait for stage 2
            while (stage != 2) { lock.wait(); }

            // now we're in stage 2
            for(int i = 20; i < 30; ++i) System.out.println(i);

            // enter stage 3
            stage = 3;
            lock.notifyAll();
        }
    }

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    Mutex.first();
                } catch (InterruptedException ex) { }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                try {
                    Mutex.second();
                } catch (InterruptedException ex) { }
            }
        }).start();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM