Item 69: Prefer concurrency utilities to wait and notify

1.  Given the difficulty of using wait and notify correctly, you should use the higher-level concurrency utilities instead. The higher-level utilities in java.util.concurrent fall into three categories: the Executor Framework, concurrent collections and synchronizers.

 

2.  The concurrent collections provide high-performance concurrent implementations of standard collection interfaces such as List, Queue, and Map. To provide high concurrency, these implementations manage their own synchronization internally. Therefore, it is impossible to exclude concurrent activity from a concurrent collection; locking it will have no effect but to slow the program.

 

3.  ConcurrentMap extends Map and adds several methods, including putIfAbsent(key, value), which inserts a mapping for a key if none was present and returns the previous value associated with the key, or null if there was none. This makes it easy to implement thread-safe canonicalizing maps.

 

4.  Some of the collection interfaces have been extended with blocking operations, which wait (or block) until they can be successfully performed. BlockingQueue extends Queue and adds several methods, including take, which removes and returns the head element from the queue, waiting if the queue is empty. This allows blocking queues to be used for work queues (also known as producer-consumer queues), to which one or more producer threads enqueue work items and from which one or more consumer threads dequeue and process items as they become available. As you’d expect, most ExecutorService implementations, including ThreadPoolExecutor, use a BlockingQueue.

 

5.  Countdown latches are single-use barriers that allow one or more threads to wait for one or more other threads to do something. The sole constructor for CountDownLatch takes an int that is the number of times the countDown method must be invoked on the latch before all waiting threads are allowed to proceed.

 

6.  For interval timing, always use System.nanoTime in preference to System.currentTimeMillis. System.nanoTime is both more accurate and more precise, and it is not affected by adjustments to the system’s real-time clock.

 

7.  The wait method is used to make a thread wait for some condition. It must be invoked inside a synchronized region that locks the object on which it is invoked. Here is the standard idiom for using the wait method:

// The standard idiom for using the wait method
synchronized (obj) {
    while (<condition does not hold>)
        obj.wait(); // (Releases lock, and reacquires on wakeup)
    // Perform action appropriate to condition
}

 

8.  Always use the wait loop idiom to invoke the wait method; never invoke it outside of a loop. The loop serves to test the condition before and after waiting. Testing the condition before waiting and skipping the wait if the condition already holds are necessary to ensure liveness. If the condition already holds and the notify (or notifyAll) method has already been invoked before a thread waits, there is no guarantee that the thread will ever wake from the wait. Testing the condition after waiting and waiting again if the condition does not hold are necessary to ensure safety. If the thread proceeds with the action when the condition does not hold, it can destroy the invariant guarded by the lock.

 

9.  There are several reasons a thread might wake up when the condition does not hold:

    1)  Another thread could have obtained the lock and changed the guarded state between the time a thread invoked notify and the time the waiting thread woke.

    2)  Another thread could have invoked notify accidentally or maliciously when the condition did not hold. 

    3)  The notifying thread could be overly “generous” in waking waiting threads.

    4)  The waiting thread could (rarely) wake up in the absence of a notify. This is known as a spurious wakeup.

 

10.  The notifyAll method should generally be used in preference to notify. Because it guarantees that you’ll wake the threads that need to be awakened.

 

11.  As an optimization, you may choose to invoke notify instead of notifyAll if all threads that could be in the wait-set are waiting for the same condition and only one thread at a time can benefit from the condition becoming true. Even if these conditions appear true, there may be cause to use notifyAll in place of notify. Because using notifyAll in place of notify protects against accidental or malicious waits by an unrelated thread. Such waits could otherwise “swallow” a critical notification, leaving its intended recipient waiting indefinitely.

 

你可能感兴趣的:(wait,notify,notifyAll)