|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 4
See Bill Venners's "Design for thread safety" for a quick tutorial on three ways to make an object thread safe. Vladimir Roubtsov's "Mutable or immutable?" defines and discusses immutable objects and patterns.
Sometimes a calculation requires mutability, with references that can't be confined to a single thread; what to do then? This situation demands synchronization, which is a kind of locking that guarantees exclusive access by a thread to a shared resource.
Syntactically, the synchronized keyword can be applied to both methods and blocks. In broad terms, block synchronization is more useful. For instance, using block synchronization would transform the code sample from Listing 3 to the following:
...
synchronized(common.balance) {
common.balance = getBalance();
if (common.balance > common.threshold) {
...
}
The synchronized keyword locks the source code segment so that only one thread can execute at a time. You are thus guaranteed that common.balance will have the same value on reading within the thread as when it was written.
Alternately, you could use a slightly different syntax to lock resources for the span of an entire method:
...
public static synchronized int getBalance() {
...
Synchronized locking guarantees that computation of getBalance() is atomic or transactional across its resources.
Synchronization is a relatively delicate matter: It applies only to blocks and methods, not variables. If mis-used it can
result in pathologies like deadlock. Synchronization applies only to final fields, and it's managed by methods like wait() and notify(). You can also configure synchronization with java.util.concurrent.locks to yield interruptible or re-entrant locks, which I discuss in the next section. (Also see Resources.)
Brian Goetz's"Avoid synchronization deadlocks" is an in-depth look at how synchronized can lead to deadlock, followed by tips for working around it.
Introductory thread communication is typically like what you saw in the RockScissorsPaper program: it travels back to (and from) the parent process. In some situations, reliable, direct communication between threads is also desirable. (For instance, many engineering and network calculations involve neighboring components, often modelled by threads deterministically exchanging data with a small number of nearby threads.)
Superficially, the simplest way for one thread to send a signal or reliable communication to another thread is called busy waiting. In busy waiting, implemented by wait/notify, the sending thread emits the signal as soon as it can, while the receiving thread blocks until the signal arrives, perhaps
by way of a variable within a synchronized segment.
Busy waiting is a species of polling -- with all of the associated inefficiencies. (See "Five ways to maximize Java NIO and NIO.2" for my recent discussion about Java I/O and polling.)
Learn more about topics related to multithreaded programming and concurrency on the Java platform.
java.util.concurrent
Callable and Runnable
Wait/notify
Thread safety