Java Reference
In-Depth Information
85
System.out.printf(
"%n%n"
);
86
}
87
}
// end class CircularBuffer
Fig. 23.18
|
Synchronizing access to a shared three-element bounded buffer. (Part 3 of 3.)
Line 5 initializes array
buffer
as a three-element
int
array that represents the circular
buffer. Variable
occupiedCells
(line 7) counts the number of elements in
buffer
that
contain data to be read. When
occupiedBuffers
is
0
, the circular buffer is
empty
and the
Consumer
must
wait
—when
occupiedCells
is
3
(the size of the circular buffer), the cir-
cular buffer is
full
and the
Producer
must
wait
. Variable
writeIndex
(line 8) indicates the
next location in which a value can be placed by a
Producer
. Variable
readIndex
(line 9)
indicates the position from which the next value can be read by a
Consumer
.
Circular-
Buffer
's instance variables are
all
part of the class's shared mutable data, thus access to all
of these variables must be synchronized to ensure that a
CircularBuffer
is thread safe.
CircularBuffer
Method
blockingPut
CircularBuffer
method
blockingPut
(lines 12-31) performs the same tasks as in
Fig. 23.16, with a few modifications. The loop at lines 17-21 determines whether the
Producer
must
wait
(i.e., all buffer cells are
full
). If so, line 19 indicates that the
Producer
is
waiting
to perform its task. Then line 20 invokes method
wait
, causing the
Producer
thread to
release
the
CircularBuffer
's
lock
and
wait
until there's space for a new value to
be written into the buffer. When execution continues at line 23 after the
while
loop, the
value written by the
Producer
is placed in the circular buffer at location
writeIndex
. Then
line 26 updates
writeIndex
for the next call to
CircularBuffer
method
blockingPut
.
This line is the key to the buffer's
circularity
. When
writeIndex
is incremented
past the
end of the buffer
, the line sets it to
0
. Line 28 increments
occupiedCells
, because there's
now one more value in the buffer that the
Consumer
can read. Next, line 29 invokes meth-
od
displayState
(lines 57-86) to update the output with the value produced, the number
of occupied buffer cells, the contents of the buffer cells and the current
writeIndex
and
readIndex
. Line 30 invokes method
notifyAll
to transition
waiting
threads to the
run-
nable
state, so that a waiting
Consumer
thread (if there is one) can now try again to read a
value from the buffer.
CircularBuffer
Method
blockingGet
CircularBuffer
method
blockingGet
(lines 34-54) also performs the same tasks as it did
in Fig. 23.16, with a few minor modifications. The loop at lines 38-42 (Fig. 23.18) de-
termines whether the
Consumer
must wait (i.e., all buffer cells are
empty
). If the
Consumer
must
wait
, line 40 updates the output to indicate that the
Consumer
is
waiting
to perform
its task. Then line 41 invokes method
wait
, causing the current thread to
release the lock
on the
CircularBuffer
and
wait
until data is available to read. When execution eventually
continues at line 44 after a
notifyAll
call from the
Producer
,
readValue
is assigned the
value at location
readIndex
in the circular buffer. Then line 47 updates
readIndex
for the
next call to
CircularBuffer
method
blockingGet
. This line and line 26 implement the
circularity
of the buffer. Line 49 decrements
occupiedCells
, because there's now one
more position in the buffer in which the
Producer
thread can place a value. Line 50 in-
vokes method
displayState
to update the output with the consumed value, the number