UnboundedQueue
enq and deq operations, not nodes
deqLock


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;
}
AtomicReferences// 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
LockFreeQueuepublic 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);}}}}