119.
private enum NodeType {ITEM, RESERVATION, SENTINEL};
public class SynchronousDualQueue<T> {
AtomicReference<Node> head;
AtomicReference<Node> tail;
private final Node NullNode = new Node(null, NodeType.SENTINEL);
/* Replace all null with NullNode */
}
120.
package p120;
public class TwoThreadLockFreeQueue<T>
{
int head = 0, tail = 0;
T[] items;
@SuppressWarnings("unchecked")
public TwoThreadLockFreeQueue(int capacity)
{
head = tail = 0;
items = (T[]) new Object[capacity];
}
public void enq(T x)
{
//rmb
while(tail - head == items.length)
{
//rmb
}
items[tail % items.length] = x;
tail ++;
//wmb
}
public Object deq()
{
//rmb
while(tail == head)
{
//rmb
}
Object x = items[head % items.length];
head ++;
//wmb
return x;
}
}
121.
package p121;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TwoLockArrayQueue
{
private final int capacity;
private int enqSideSize;
private int deqSideSize;
private Lock enqLock;
private Condition enqCond;
private Lock deqLock;
private Condition deqCond;
private int head;
private int tail;
private T[] datas;
@SuppressWarnings("unchecked")
public TwoLockArrayQueue(int cap)
{
capacity = cap;
enqSideSize = 0;
deqSideSize = 0;
head = 0;
tail = 0;
enqLock = new ReentrantLock();
deqLock = new ReentrantLock();
enqCond = enqLock.newCondition();
deqCond = deqLock.newCondition();
datas = (T[]) new Object[capacity];
}
public void enq(T x)
{
boolean notifyDeq = false;
enqLock.lock();
try
{
while(enqSideSize == capacity)
{
deqLock.lock();
try
{
enqSideSize = capacity - deqSideSize;
deqSideSize = 0;
}finally
{
deqLock.unlock();
}
if(enqSideSize == capacity)
{
enqCond.await();
}
}
datas[tail % capacity] = x;
tail ++;
enqSideSize ++;
//No notification lost as deqSideSize can only increase and still fit the condition
if(enqSideSize - deqSideSize <= 1)
{
notifyDeq = true;
}
}catch(InterruptedException e)
{
e.printStackTrace();
}finally
{
enqLock.unlock();
}
if(!notifyDeq) return;
deqLock.lock();
try
{
deqCond.signalAll();
}finally
{
deqLock.unlock();
}
}
public T deq()
{
T x = null;
boolean notifyEnq = false;
deqLock.lock();
try
{
/*
* No notification lost as when enqSideSize increased in between while and await,
* there would always an enq thread knows enqSideSize - deqSideSize <= 1
* and requires deqLock for notification.
*/
while(deqSideSize >= enqSideSize)
{
deqCond.await();
}
x = datas[head % capacity];
head ++;
deqSideSize ++;
if(enqSideSize >= capacity)
{
notifyEnq = true;
}
}catch(InterruptedException e)
{
e.printStackTrace();
}finally
{
deqLock.unlock();
}
if(notifyEnq)
{
enqLock.lock();
try
{
enqCond.signalAll();
}finally
{
enqLock.unlock();
}
}
return x;
}
}
122. 必须。head.next != null --> deq 必须是原子的,否则可能有这样的case:queue中只有一个元素,2个线程deq,2个现成都得到head.next != null,然后2个现成分别在拿到锁之后deq,错误。
123.
使用SynchronousDualQueue, 在enq或deq消除(而不是加入list)的时候排除不符合要求的case。是分散的,低征用的,随机的。
package p123;
import java.util.concurrent.atomic.AtomicReference;
public class FeedOthers
{
AtomicReference
head;
AtomicReference
tail;
public FeedOthers() {
Node sentinel = new Node(null, NodeType.ITEM);
head = new AtomicReference
(sentinel);
tail = new AtomicReference
(sentinel); } public void enq(T e) { Node offer = new Node(e, NodeType.ITEM); while (true) { Node t = tail.get(); Node h = head.get(); if (h == t || t.type == NodeType.ITEM) { Node n = t.next.get(); if (t == tail.get()) { if (n != null) { tail.compareAndSet(t, n); } else if (t.next.compareAndSet(n, offer)) { tail.compareAndSet(t, offer); while (offer.item.get() == e); // spin h = head.get(); if (offer == h.next.get()) { head.compareAndSet(h, offer); } return; } } } else { Node n = h.next.get(); if (t != tail.get() || h != head.get() || n == null) { continue; // inconsistent snapshot } //fulltopic else if(n.type != NodeType.RESERVATION) { continue; } T other = n.item.get(); if(other.equals(e)) { // System.out.println("Can not eat from self "); continue; } boolean success = n.item.compareAndSet(other, e); head.compareAndSet(h, n); if (success) { return; } } } } public T deq(T me) { Node offer = new Node(me, NodeType.RESERVATION); //fulltopic while (true) { Node t = tail.get(); Node h = head.get(); if (h == t || t.type == NodeType.RESERVATION) { Node n = t.next.get(); if (t == tail.get()) { if (n != null) { tail.compareAndSet(t, n); } else if (t.next.compareAndSet(n, offer)) { tail.compareAndSet(t, offer); while (offer.item.get() == me); // spin h = head.get(); if (offer == h.next.get()) { head.compareAndSet(h, offer); } return offer.item.get(); } } } else { Node n = h.next.get(); if (t != tail.get() || h != head.get() || n == null) { continue; // inconsistent snapshot } //fulltopic else if(n.type != NodeType.ITEM) { continue; } T item = n.item.get(); if(item == null || item.equals(me)) { // System.out.println("Can not feed self "); continue; } boolean success = n.item.compareAndSet(item, null); head.compareAndSet(h, n); if (success) { return item; } } } } private enum NodeType {ITEM, RESERVATION}; private class Node { volatile NodeType type; volatile AtomicReference
item; volatile AtomicReference
next; Node(T item, NodeType type) { this.item = new AtomicReference
(item); this.next = new AtomicReference
(null); this.type = type; } public String toString() { return "Node[" + type + ", item: " + item + ", next: " + next + "]"; } } }
124.
1. 应该以38行成功为线性化点,否则,
假设queue的状况是 head(A)-->B-->C-->sentinel
2个线程deq的执行顺序是 ThreadA.CAS --> ThreadB.CAS --> ThreadB.return --> ThreadA.return
以return为线性化点,返回结果应该是ThreadB得到A,ThreadA得到B; 但是算法的结果是 ThreadA得到A,ThreadB得到B。
2. enq的线性化点位16行成功。如果以tail更新为可线性化点,假设一个线程16行执行部成功,但是21行更新tail成功,则这个方法在没有实际enq的情况下使是方法的执行状态可见,这没有意义。
125.
1. enq是无等待的,因为每个调用能够在有限步完成。deq不是无锁的,假设只有一个deq线程,没有enq,这个线程运行无限步也不能完成一个调用。
2. 大概是这样:
deq()的可线性化点在13行成功地时刻。
对于enq和deq的操作,enq的可线性化点在第7行执行结束。
对于enq和enq的操作,enq的可线性化点在它们enq的值被deq成功的时刻,即第13行操作成功的时刻。
Reference: Reading the Linearizability paper of Herlihy and Wing