Java Reference
In-Depth Information
23.4.4 Synchronized Mutable Data Sharing—Making Operations
Atomic
The output errors of Fig. 23.7 can be attributed to the fact that the shared object, Simple-
Array , is not thread safe SimpleArray is susceptible to errors if it's accessed concurrently
by multiple threads . The problem lies in method add , which stores the value of writeIndex ,
places a new value in that element, then increments writeIndex . Such a method would
present no problem in a single-threaded program. However, if one thread obtains the value
of writeIndex , there's no guarantee that another thread cannot come along and increment
writeIndex before the first thread has had a chance to place a value in the array. If this hap-
pens, the first thread will be writing to the array based on a stale value of writeIndex —a
value that's no longer valid. Another possibility is that one thread might obtain the value
of writeIndex after another thread adds an element to the array but before writeIndex is
incremented. In this case, too, the first thread would write to the array based on an invalid
value for writeIndex .
SimpleArray is not thread safe because it allows any number of threads to read and modify
shared mutable data concurrently , which can cause errors. To make SimpleArray thread safe,
we must ensure that no two threads can access its shared mutable data at the same time.
While one thread is in the process of storing writeIndex , adding a value to the array, and
incrementing writeIndex , no other thread may read or change the value of writeIndex or
modify the contents of the array at any point during these three operations. In other words,
we want these three operations—storing writeIndex , writing to the array, incrementing
writeIndex —to be an atomic operation , which cannot be divided into smaller subopera-
tions. (As you'll see in later examples, read operations on shared mutable data should also be
atomic.) We can simulate atomicity by ensuring that only one thread carries out the three
operations at a time. Any other threads that need to perform the operation must wait until
the first thread has finished the add operation in its entirety.
Atomicity can be achieved using the synchronized keyword. By placing our three
suboperations in a synchronized statement or synchronized method, we allow only one
thread at a time to acquire the lock and perform the operations. When that thread has
completed all of the operations in the synchronized block and releases the lock, another
thread may acquire the lock and begin executing the operations. This ensures that a thread
executing the operations will see the actual values of the shared mutable data and that these
values will not change unexpectedly in the middle of the operations as a result of another thread's
modifying them .
Software Engineering Observation 23.5
Place all accesses to mutable data that may be shared by multiple threads inside
synchronized statements or synchronized methods that synchronize on the same lock.
When performing multiple operations on shared mutable data, hold the lock for the
entirety of the operation to ensure that the operation is effectively atomic.
Class SimpleArray with Synchronization
Figure 23.8 displays class SimpleArray with the proper synchronization. Notice that it's
identical to the SimpleArray class of Fig. 23.5, except that add is now a synchronized
method (line 20). So, only one thread at a time can execute this method. We reuse classes
ArrayWriter (Fig. 23.6) and SharedArrayTest (Fig. 23.7) from the previous example.
 
 
Search WWH ::




Custom Search