UnboundedQueue
enq
and deq
operations, not nodesdeqLock
head
enqLock
tail.next
tail
UnboundedQueue
in Codepublic class UnboundedQueue<T> implements SimpleQueue<T> {
final ReentrantLock enqLock;
final ReentrantLock deqLock;
volatile Node head;
volatile Node tail;
public UnboundedQueue() {
head = new Node(null); tail = head;
enqLock = new ReentrantLock();
deqLock = new ReentrantLock(); }
...
}
Node
Class class Node {
final T value;
volatile Node next;
public Node (T value) {
this.value = value;
}
}
enq
Method public void enq (T value) {
enqLock.lock();
try {
Node nd = new Node(value);
tail.next = nd;
tail = nd;
} finally {
enqLock.unlock();
}
}
deq
Method public T deq() throws EmptyException {
T value;
deqLock.lock();
try {
if (head.next == null){throw new EmptyException();}
value = head.next.value;
head = head.next;
return value;
} finally {
deqLock.unlock();
}
}
UnboundedQueue
Linearizable? public void enq (T value) {
Node nd = new Node(value);
tail.next = nd;
tail = nd;
}
public T deq() throws EmptyException {
if (head.next == null){throw new EmptyException();}
value = head.next.value;
head = head.next;
return value;
}
UnboundedQueue
:
enq
and deq
enq
and deq
locks, both terminate in a finite number of steps independent of the actions of the otherenq
(or deq
) are blocking: a thread cannot make progress while another thread holds the lockIs it possible to implement a (sequentially consistent? linearizable?) queue without locks?
What could go wrong with concurrent enq
?
public void enq (T value) {
Node nd = new Node(value);
tail.next = nd;
tail = nd;
}
public void enq (T value) {
Node nd = new Node(value);
tail.next = nd;
tail = nd;
}
AtomicReference
s// an AtomicReference pointing to someNode
var nd = new AtomicReference<Node>(someNode);
// try to update nd to refer to updated
nd.compareAndSet(expected, update);
Effect of compareAndSet(expected, update)
:
nd
’s current value is expected
, then update value to update
true
nd
’s current value is not expected
, do not update its value
false
public void enq (T value) {
Node nd = new Node(value);
tail.next = nd;
tail = nd;
}
To do:
tail.next
to nd
tail
to nd
How?
tail.next
only if tail.next == null
tail.next
to nd
:
last
to tail
, next
to tail.next
last
is still null
last.next
to nd
only if last.next
is still null
tail
to next
LockFreeQueue
public class LockFreeQueue<T> implements SimpleQueue<T> {
private AtomicReference<Node> head;
private AtomicReference<Node> tail;
...
public void enq(T item) {...}
public T deq() throws EmptyException {...}
class Node {
public T value;
public AtomicReference<Node> next;
...
}
}
enq
public void enq(T item) {
if (item == null) throw new NullPointerException();
Node node = new Node(item);
while (true) {
Node last = tail.get();
Node next = last.next.get();
if (last == tail.get()) {
if (next == null) {
if (last.next.compareAndSet(next, node))
tail.compareAndSet(last, node); return;
} else {
tail.compareAndSet(last, next);}}}}
public void enq(T item) {
if (item == null) throw new NullPointerException();
Node node = new Node(item);
while (true) {
Node last = tail.get();
Node next = last.next.get();
if (last == tail.get()) {
if (next == null) {
if (last.next.compareAndSet(next, node))
tail.compareAndSet(last, node); return;
} else {
tail.compareAndSet(last, next);}}}}