126.
package p126;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class UnboundListStack
{
public class Node
{
private T item;
public Node next;
public Node(T item)
{
this.item = item;
this.next = null;
}
public T get()
{
return item;
}
}
private Lock lock;
private Node head;
public UnboundListStack()
{
this.lock = new ReentrantLock();
head = new Node(null);
}
public void push(T x)
{
Node node = new Node(x);
lock.lock();
try
{
node.next = head.next;
head.next = node;
}finally
{
lock.unlock();
}
}
public T pop() throws Exception
{
lock.lock();
try
{
if(head.next == null)
{
throw new Exception("Empty");
}else
{
T item = head.next.get();
head.next = head.next.next;
return item;
}
}finally
{
lock.unlock();
}
}
}
package p126;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class UnboundListStack
{
public class Node
{
private T item;
public Node next;
public Node(T item)
{
this.item = item;
this.next = null;
}
public T get()
{
return item;
}
}
private Lock lock;
private Node head;
public UnboundListStack()
{
this.lock = new ReentrantLock();
head = new Node(null);
}
public void push(T x)
{
Node node = new Node(x);
lock.lock();
try
{
node.next = head.next;
head.next = node;
}finally
{
lock.unlock();
}
}
public T pop() throws Exception
{
lock.lock();
try
{
if(head.next == null)
{
throw new Exception("Empty");
}else
{
T item = head.next.get();
head.next = head.next.next;
return item;
}
}finally
{
lock.unlock();
}
}
}
128.
package p128;
import java.util.concurrent.atomic.AtomicStampedReference;
import utils.EmptyException;
import ch7.Backoff;
public class LockFreeStack
{
public class Node
{
public T value;
public Node next;
public Node(T value)
{
this.value = value;
next = null;
}
}
static final int INITCAPACITY = 2;
ThreadLocal
nodeList = new ThreadLocal
()
{
protected Node initialValue()
{
Node head = new Node(null);
for(int i = 0; i < INITCAPACITY; i ++)
{
Node node = new Node(null);
node.next = head.next;
head.next = node;
}
return head;
}
};
private Node allocNode()
{
Node head = nodeList.get();
if(head.next == null)
{
return new Node(null);
}else
{
Node node = head.next;
head.next = head.next.next;
node.next = null;
return node;
}
}
private void freeNode(Node node)
{
if(node == null)
{
return;
}
node.value = null;
Node head = nodeList.get();
node.next = head.next;
head.next = node;
}
AtomicStampedReference
top = new AtomicStampedReference
(null, 0); static final int MIN_DELAY = 1; static final int MAX_DELAY = 10; Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY); protected boolean tryPush(Node node) { int[] stamp = new int[1]; Node oldTop = top.get(stamp); node.next = oldTop; return (top.compareAndSet(oldTop, node, stamp[0], stamp[0] + 1)); } public void push(T value) { Node node = allocNode(); node.value = value; while(true) { if(tryPush(node)) { return; }else { try { backoff.backoff(); }catch(Exception e) { e.printStackTrace(); } } } } protected Node tryPop() throws EmptyException { int[] stamp = new int[1]; Node oldTop = top.get(stamp); if(oldTop == null) { throw new EmptyException(); } Node newTop = oldTop.next; if(top.compareAndSet(oldTop, newTop, stamp[0], stamp[0] + 1)) { return oldTop; }else { return null; } } public T pop() throws EmptyException { while(true) { Node returnNode = tryPop(); if(returnNode != null) { T value = returnNode.value; freeNode(returnNode); return value; }else { try { backoff.backoff(); }catch(Exception e) { e.printStackTrace(); } } } } }
129.
1. 意义在于push和pop都是在top上的冲突,所以他们共享同一个backoff才能比较好的减少冲突。
2. 根据:add additional backoff delays before accessing the shared stack, and control whether to access the shared stack or the array dynamically。在policy中增加关于tryPush()和tryPop()成功和失败的计数。假设成功次数为s,失败次数为f,则成功率为 s / (s + f);则在tryPush()之前生成随机数r,如果 r % ((s + f) / s) == 0,则直接尝试tryPush(),否则直接从exchangers 入栈。
130.
将EliminationBackoffStack去掉tryPush和tryPop的部分,并将exchangers' capacity设为题目要求的界限。
131.
有类似于ABA的问题,比如:
1. top = 2, --> T1.pop() --> top = 1 --> T1.pop.i = 2, stack[2].value = value.T0
==> T2.pop() --> top = 0
==> T3.push() --> top = 1
==> T4.push() --> top = 2--> stack[2].value = value.T4; stack[2].full = true
==> T1.stack[2].full == true --> T1.pop() = value.T4
即T0写入的值被覆盖,丢失了。
2. top = 2 --> T1.pop --> top = 1 --> T1.i = 2
==> T2.push --> top = 2 --> T2.pushed
==> T3.pop --> top = 1, T3.i = 2
==> T3 && T1 pop the same item
问题在于push线程和pop线程之间没有同步,push线程并不知道当前操作的slot是否已经被pop了,即不能做到1对1。多个线程可能得到同一个index,因为取得index和读写操作并不原子,所以写/读的时候没有互斥;另外如果pop和push的速度太失衡,比如top.increase的速度总是赶不上pop.decrease的速度,会活锁。
package p131;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import utils.EmptyException;
import utils.FullException;
public class DualStack
{
public class Slot
{
public static final int EMPTY = 0;
public static final int FULL = 1;
public static final int BUSY = 2;
volatile T value = null;
AtomicStampedReference
state = new AtomicStampedReference
(EMPTY, 0);
}
Slot[] stack;
int capacity;
private AtomicInteger top = new AtomicInteger(0);
@SuppressWarnings("unchecked")
public DualStack(int myCapacity)
{
this.capacity = myCapacity;
stack = new DualStack.Slot[capacity];
// stack = (Slot[]) new Object[capacity];
for(int i = 0; i < capacity; i ++)
{
stack[i] = new Slot();
}
}
public void push(T value) throws FullException
{
while(true)
{
int i = top.getAndIncrement();
if(i > capacity - 1)
{
top.compareAndSet(i, capacity);
throw new FullException("" + i);
}else if(i >= 0)
{
while(stack[i].state.getReference().intValue() != Slot.EMPTY) {}
int[] stamp = new int[1];
@SuppressWarnings("unused")
int state = stack[i].state.get(stamp);
if(!stack[i].state.compareAndSet(Slot.EMPTY, Slot.BUSY, stamp[0], stamp[0] + 1))
{
continue;
}
stack[i].value = value;
stack[i].state.set(Slot.FULL, stamp[0] + 1);
// System.out.println("stack " + i + " set full");
return;
}
}
}
public T pop() throws EmptyException
{
while(true)
{
int i = top.getAndDecrement() - 1;
if(i < 0)
{
top.compareAndSet(i + 1, 0);
throw new EmptyException("" + (i + 1));
}else if(i < capacity)
{
// System.out.println("Try to pop " + i);
while(stack[i].state.getReference().intValue() != Slot.FULL) {}
int[] stamp = new int[1];
@SuppressWarnings("unused")
int state = stack[i].state.get(stamp);
T value = stack[i].value;
if(!stack[i].state.compareAndSet(Slot.FULL, Slot.EMPTY, stamp[0], stamp[0]))
{
continue;
}
return value;
}
}
}
public int get()
{
return top.get();
}
}
132.
1. 有131题类似的问题;还有越界的问题。比如:
T1.pop, top = -1 --> T2.pop, top = -2 --> T3.push, top = -1, items[-1] = x;
2. 如下所示。Rooms保证在所有的线程在由push和pop工作的转换中是静态一致的,所以保证操作的i都是有效的。同时如果2个push线程得到同一个index,他们之间必然已经隔了一段pop的执行;而在这个pop之前,第一个push线程必然已经完成items[i]=x的操作。如果这个slot没有被pop,则在第二次push阶段因为i = top.getAndIncrement()也不会被覆盖。
package p132;
import java.util.concurrent.atomic.AtomicInteger;
import p97.Rooms;
import utils.EmptyException;
import utils.FullException;
public class RoomsStack
{
private AtomicInteger top;
private T[] items;
private Rooms rooms;
public static final int PUSH = 0;
public static final int POP = 1;
@SuppressWarnings("unchecked")
public RoomsStack(int capacity)
{
top = new AtomicInteger(0);
items = (T[]) new Object[capacity];
rooms = new Rooms(2);
}
public void push(T x) throws FullException
{
rooms.enter(PUSH);
try
{
int i = top.getAndIncrement();
if(i >= items.length)
{
top.getAndDecrement();
throw new FullException();
}
items[i] = x;
}finally
{
rooms.exit();
}
}
public T pop() throws EmptyException
{
rooms.enter(POP);
try
{
int i = top.getAndDecrement();
if(i < 0)
{
top.getAndIncrement();
throw new EmptyException();
}
return items[i];
}finally
{
rooms.exit();
}
}
}
133.
private class ExitHandler implements Rooms.Handler
{
public void onEmpty()
{
int newSize = items.length;
@SuppressWarnings("unchecked")
T[] newItems = (T[]) new Object[newSize];
for(int i = 0 ; i < items.length; i ++)
{
newItems[i] = items[i];
}
items = newItems;
}
}
@SuppressWarnings("unchecked")
public RoomsStack(int capacity)
{
top = new AtomicInteger(0);
items = (T[]) new Object[capacity];
rooms = new Rooms(2);
rooms.setExitHandler(PUSH, new ExitHandler());
}
public void push(T x)
{
while(true)
{
rooms.enter(PUSH);
int i = top.getAndIncrement();
if(i >= items.length)
{
top.getAndDecrement();
rooms.exit();
}else
{
items[i] = x;
return;
}
}
}