push(x)
, pop()
An execution of a shared object is linearizable if:
An implementation of an object is linearizable if every execution is linearizable.
TwoCounter
public class TwoCounter {
int[] counts = new int[2];
public void increment (int amt) {
int i = ThreadID.get(); // thread IDs are 0 and 1
int count = counts[i];
counts[i] = count + amt;
}
public int read () {
int count = counts[0];
count = count + counts[1];
return count;
}}
ThreeCounter
Examplepublic class ThreeCounter {
int[] counts = new int[3];
public void increment (int amt) {
int i = ThreadID.get(); // thread IDs are 0, 1, and 2
int count = counts[i];
counts[i] = count + amt;
}
}
read
Method public int read () {
int count = counts[0];
count = count + counts[1];
count = count + counts[2];
return count;
}
ThreeCounter
Linearizable? public int read () {
int count = counts[0];
count = count + counts[1];
count = count + counts[2];
return count;
}
Questions.
ThreeCounter
sequentially consistent?Question. How to implement a (non-concurrent) queue with a linked list?
Node head
sentinal
deq
returns head.next
value (if any), updates head
Node tail
enq
updates tail.next
, updates tail
enqLock
locks enq
operationdeqLock
locks deq
operationNode
s are not lockeddeqLock
head
enqLock
tail.next
tail
Why do we need the sentinel node?
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;
}
Concurrent queues without locks?!?!