382
} catch (InterruptedException e) {
return;
}
processItem (itemToProcess);
}
}
The new code now can “escape” the infinite loop. You can call thread.interrupt()
from another thread, which throws the InterruptedException that is then caught by the
main thread’s catch clause. The infinite loop can be exited within this clause.
InterruptedExceptions are a way of sending extra information to waiting
(or sleeping) threads so that they may handle a different scenario (for example,
an orderly program shutdown).
For this reason, every operation that changes the
state of the thread to sleep/wait must be surrounded by a try/catch block that can
catch the InterruptedException. This is one of the cases in which the exception
(InterruptedException) is not an error but more of a way of signaling between
threads that something has occurred that requires attention.
Solution 1 demonstrates the most common (oldest) form of coordination. The
solution requires making a thread wait and suspending execution until the thread gets
notified (or awakened) by another thread.
For solution 1 to work, the originating thread needs to acquire a lock. This lock will
then be the “phone number” on which another thread can notify the originating thread
to wake up. After the originating thread acquires the lock (phone number),
it proceeds to
wait. As soon as the wait() method is called, the lock is released, allowing other threads
to acquire the same lock. The secondary thread then proceeds to acquire the lock (the
phone number) and notifies (which would be like dialing a wake-up call) the originating
thread. After the notification, the originating thread resumes execution.
In the solution 1 code, the lock is a dummy object identified as objectToSync. In
practice, the object on which locks are waiting and notifying could be any valid instance
object in Java; for example, we could have used the this reference
to make the main
thread wait.
The main advantage of using this technique is the explicitness of controlling whom
to wait and when to notify (and the ability to notify all threads waiting on the same
object; see the following tip).
Chapter 10 ConCurrenCy
383
Tip Multiple threads can wait on the same lock (same phone number to be
awakened). When a secondary thread calls the notify() method, it wakes
up one of the “waiting” threads (there is no fairness about which is awakened).
Sometimes you need to notify all the threads; you can call the notifyAll()
method instead of the notify() method. this is mostly used when preparing
many
threads to take some work, but the work is not yet finished setting up.
Solution 2 uses a more modern approach to notification, as it involves a
CountDownLatch. When setting up, specify the number of “counts” the latch will have.
The main thread then waits (stop execution) by calling the CountDownLatch’s await()
method until the latch counts down to 0. When the latch reaches 0, the main thread
wakes up and continues execution. As the worker thread completes, call the latch.
countdown() method, which decrements the latch’s current count value. If the latch’s
current value reaches 0, the main thread waiting on the CountDownLatch wakes up and
continues execution.
The main advantage of using CountDownLatches is that it is possible to spawn
multiple tasks simultaneously and wait for all of them to complete. (In
the solution
example, we didn’t need to wait until one or the other threads were completed before
continuing; they all were started, and when the latch was 0, the main thread continued.)
Solution 3 lets you access to the thread you want to wait on. For the main thread,
it’s just a matter of calling the secondary thread’s join() method. Then the main thread
waits (stop executing) until the secondary thread completes.
The advantage of this method is that it doesn’t require the secondary threads to
know any synchronization mechanism. As long as the secondary thread terminates
execution, the main thread can wait on them.
Do'stlaringiz bilan baham: