Concurrent Queues:
Download and test the code yourself:
Basic operations
void push(T item)
add a new item to the top of the stackT pop()
remove top item from the stack and return it
EmptyException
if stack was emptypush()
Step 1: Create Nodepush()
Step 2: Set next
push()
Step 3: Set head
push()
Completepop()
?pop()
Step 1: Store value
pop()
Step 2: Update head
pop()
Step 3: Return value
With locks:
head
, coarse locking is natural choiceWithout locks?
Use linked-list implementation
top
as an AtomicReference<Node>
compareAndSet
to modify top
top
points to item’s Node
public class LockFreeStack<T> implements SimpleStack<T> {
AtomicReference<Node> top = new AtomicReference<Node>(null);
public void push(T item) {...}
public T pop() throws EmptyException {...}
class Node {
public T value;
public AtomicReference<Node> next;
public Node(T value) {
this.value = value;
this.next = new AtomicReference<Node>(null);
}
}
}
push
public void push(T item) {
Node nd = new Node(item);
Node oldTop = top.get();
nd.next.set(oldTop);
while (!top.compareAndSet(oldTop, nd)) {
oldTop = top.get();
nd.next.set(oldTop);
}
}
pop
public T pop() throws EmptyException {
while (true) {
Node oldTop = top.get();
if (oldTop == null) {
throw new EmptyException();
}
Node newTop = oldTop.next.get();
if (top.compareAndSet(oldTop, newTop)) {
return oldTop.value;
}
}
}
Modifying top
push
/pop
rate limited by top.compareAndSet(...)
… or is it?
Consider several concurrent accesses to a stack:
stk.push(item1)
stk.push(item2)
stk.pop()
stk.push(item4)
stk.pop()
stk.pop()
Trick Question. What is the state of stk
after these calls? What do pop
calls return?
pop
s1
to Thread 54
to Thread 3push
es 2
Note: Steps 1, 2, and 3 can be performed in parallel!
The stack was just slowing us down
push
/pop
to stack
push
, try to find a pop
and give them your valuepop
, try to find a push
and take their valueThis strategy is called elimination
push
/pop
The Exchanger
object:
exchange(T item,...)
...
parameters for timeoutSpecification of Exchanger<T> ex
:
ThreadA
calls ex.exchange(itemA,...)
ThreadB
calls ex.exchange(itemB,...)
ThreadA
receives itemB
ThreadB
receives itemA
ex.exchange(null,...)
to indicate pop
ex.exchange(item,...)
to indicate push(item)
Exchange a success if:
ThreadA
calls ex.exchange(item)
null
(i.e., other call was a pop
)ThreadB
calls ex.exchange()
item != null
(i.e. other call was push(item)
)Store an array of several Exchanger
instances
push
/pop
to stack
Exchanger ex
in arrayex.exchange(null)
for pop()
ex.exchange(item)
for push()
Exchanger
array be?Question. When might this approach be practical?
Suppose you’re designing an airplane
How to design around this issue?
Have multiple duplicate, independent systems
The end of our worries?
Suppose all systems working normally, but
What do we do?
Have multiple systems with different inputs
0
= decrease thrust1
= increase thrustGoal:
Consensus Problem:
Coming up: How can we achieve consensus if some processes might fail?