Discussion 13: Synchronization
Download Solution Code download
Exercise 1:
Race Conditions
Suppose we have a
You can also assume that, at this point,
Draw the possible states of the
RingBuffer object buffer containing Integers, and the current state of the array backing store for our buffer looks like this:
iHead == 0 and iTail == 3.
Then, suppose the following enqueue calls are executed on separate threads:
|
|
|
|
buffer (specifically, the backing array and iTail) after these calls are executed.
There are two possible outcomes where invariants are maintained/the program behaves as intended:
and
For both of these, iTail == 0.
There are several pathological cases:
It is possible that only one update gets written, and iTail only gets incremented once (iTail == 4). This happens when both threads read iTail == 3 and perform their updates without seeing the other thread’s updates, leading to the first update being overwritten.
It is also possible for the array states to be the same as above, but iTail gets incremented twice (iTail == 0). This is because in our implementation, the array modification is performed first, and then iTail is incremented, so there exists an interleaving where the second executing thread does not read the new iTail value when adding to the array, but does read it when incrementing iTail.
Exercise 2:
Concurrent Ring Buffer
Now look at the
ConcurrentRingBuffer.java file in the release code. Your task is to implement thread-safe enqueue() and dequeue() functions (TODOs 1-2). Your implementations should block if there is nothing to be done; that is, if there is no space in the queue to add a new element, or if there is no element in the queue to be consumed.
|
|
|
|