Java Reference
In-Depth Information
from the shared object . The producer/consumer relationship separates the task of identify-
ing work to be done from the tasks involved in actually carrying out the work.
Examples of Producer/Consumer Relationship
One example of a common producer/consumer relationship is print spooling . Although
a printer might not be available when you want to print from an application (i.e., the pro-
ducer), you can still “complete” the print task, as the data is temporarily placed on disk
until the printer becomes available. Similarly, when the printer (i.e., a consumer) is avail-
able, it doesn't have to wait until a current user wants to print. The spooled print jobs can
be printed as soon as the printer becomes available. Another example of the producer/con-
sumer relationship is an application that copies data onto DVDs by placing data in a fixed-
size buffer, which is emptied as the DVD drive “burns” the data onto the DVD.
Synchronization and State Dependence
In a multithreaded producer/consumer relationship, a producer thread generates data and
places it in a shared object called a buffer . A consumer thread reads data from the buffer.
This relationship requires synchronization to ensure that values are produced and con-
sumed properly. All operations on mutable data that's shared by multiple threads (e.g., the
data in the buffer) must be guarded with a lock to prevent corruption, as discussed in
Section 23.4. Operations on the buffer data shared by a producer and consumer thread are
also state dependent —the operations should proceed only if the buffer is in the correct
state. If the buffer is in a not-full state , the producer may produce; if the buffer is in a not-
empty state , the consumer may consume. All operations that access the buffer must use syn-
chronization to ensure that data is written to the buffer or read from the buffer only if the
buffer is in the proper state. If the producer attempting to put the next data into the buffer
determines that it's full, the producer thread must wait until there's space to write a new
value. If a consumer thread finds the buffer empty or finds that the previous data has al-
ready been read, the consumer must also wait for new data to become available. Other ex-
amples of state dependence are that you can't drive your car if its gas tank is empty and
you can't put more gas into the tank if it's already full.
Logic Errors from Lack of Synchronization
Consider how logic errors can arise if we do not synchronize access among multiple
threads manipulating shared mutable data. Our next example (Figs. 23.9-23.13) imple-
ments a producer/consumer relationship without the proper synchronization . A producer
thread writes the numbers 1 through 10 into a shared buffer—a single memory location
shared between two threads (a single int variable called buffer in line 6 of Fig. 23.12 in
this example). The consumer thread reads this data from the shared buffer and displays the
data. The program's output shows the values that the producer writes (produces) into the
shared buffer and the values that the consumer reads (consumes) from the shared buffer.
Each value the producer thread writes to the shared buffer must be consumed exactly
once by the consumer thread. However, the threads in this example are not synchronized.
Therefore, data can be lost or garbled if the producer places new data into the shared buffer
before the consumer reads the previous data . Also, data can be incorrectly duplicated if the
consumer consumes data again before the producer produces the next value. To show
these possibilities, the consumer thread in the following example keeps a total of all the
values it reads. The producer thread produces values from 1 through 10. If the consumer
 
Search WWH ::




Custom Search