3

I'm trying to understand the synchronized() blocks in the program I wrote at the end of this post.

There are two threads (o and k) that use a shared lock object as a monitor for wait/notify.

o waits for k to start, inside the following synchronized block:

synchronized (lock) {
    lock.wait(); // wait for K to be ready
}

k then notifies o and waits for it to print inside this block:

synchronized (lock) {
    lock.notify(); // tell O to print
    lock.wait(); // wait for O to print
}

My question is how can k enter the synchronized block with lock? Shouldn't o own lock (since it called wait())? The Java Tutorial says:

As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.

Here's the full program:

public class OK implements Runnable {

    private static final Object lock = new Object(); // monitor for wait/notify

    private boolean isO;

    public OK(boolean b) {
        isO = b;
    }

    public static void main(String[] args) throws InterruptedException {

        Thread o = new Thread(new OK(true));
        Thread k = new Thread(new OK(false));
        o.start();
        k.start();
        k.join(); // when k is done, we're done
        System.out.println("Done.");

    }

    public void run() {

        // run method is called for both o and k, so we separate the logic
        try {
            if (isO) {
                doO();
            } else {
                doK();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    // O thread logic
    private void doO() throws InterruptedException {

        // K needs to be ready before I start
        synchronized (lock) {
            lock.wait(); // wait for K to be ready
        }

        System.out.print("O");
        synchronized (lock) {
            lock.notify(); // tell K I printed
        }
    }

    // K thread logic
    private void doK() throws InterruptedException {

        // O is waiting for me to start

        synchronized (lock) {
            lock.notify(); // tell O to print
            lock.wait(); // wait for O to print
        }
        System.out.println("K");
    }

}
4

3 回答 3

6

lock.wait releases the monitor. See the Object.wait() javadoc:

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

Intuition may tell you that "synchronized (lock)" means that it hold that lock during the entire block it wraps; but that is not how it works.

于 2013-03-24T05:40:42.883 回答
2

In addition to @Wouter Coekaerts answer, it is considered good practise to always have a call to wait() inside a loop since "spurious wake-ups" might happen. See: THI03-J. Always invoke wait() and await() methods inside a loop for example.

于 2013-03-24T05:45:00.477 回答
1

The behavior is not assured/guaranteed when it comes to multi-threading. It completely depends on the scheduler which invokes thread from a pool of threads under execution. You cannot guarantee at all that Thread O will be invoked before thread K just because you have started it before thread K. It may also even happen that scheduler picks up Thread K starts executing it first and hence it acquires lock before Thread O.

于 2013-03-24T05:43:47.097 回答