77. 将确定顺序规范的对象变成确定顺序规范:
做不确定顺序规范到确定顺序规范的映射或者将Node变成ConsensusNode,即给Node一个consensus wrapper,保证不确定的输出将在各个线程中的道一致的确定的输出。
78. 无等待的算法会出错。seq == 0会被当作需要加到list当中的Node ==> tail->next = tail.
79. 不明白“不用通用构造”又“改造这个算法”是什么意思。还是用的通用构造
package p79;
import java.util.concurrent.atomic.AtomicReference;
public class Consensus<T>
{
private AtomicReference<T> curObj;
public Consensus()
{
this.curObj = new AtomicReference<T>();
curObj.set(null);
}
public T decide(T next)
{
curObj.compareAndSet(null, next);
return curObj.get();
}
}
package p79;
public class Node
{
public Node next;
public int seq;
public int exp;
public int v;
public Consensus<Node> decideNext;
public Node()
{
this.seq = 0;
this.next = null;
decideNext = new Consensus<Node>();
}
public Node(int exp, int replace)
{
this();
this.exp = exp;
this.v = replace;
}
public static Node max(Node[] array)
{
Node max = array[0];
for(int i = 1; i < array.length; i ++)
{
if(max.seq < array[i].seq)
{
max = array[i];
}
}
return max;
}
}
package p79;
public class Universal_CAS
{
private Node[] announce;
private Node[] head;
private Node tail;
private int n;
private static ThreadLocal<Integer> ThreadId = new ThreadLocal<Integer>()
{
protected Integer initialValue()
{
return new Integer(0);
}
};
public Universal_CAS(int n)
{
this.n = n;
tail = new Node();
tail.seq = 1;
announce = new Node[n];
head = new Node[n];
for(int i = 0; i < n; i ++)
{
announce[i] = tail;
head[i] = tail;
}
}
public int read()
{
Node max = Node.max(head);
return max.v;
}
public boolean compareAndSet(int exp, int replace)
{
int i = ThreadId.get();
announce[i] = new Node(exp, replace);
head[i] = Node.max(head);
Node before = null;
while(announce[i].seq == 0)
{
before = head[i];
Node help = announce[(before.seq + 1) % n];
Node prefer = help;
if(help.seq != 0)
{
prefer = announce[i];
}
Node after = before.decideNext.decide(prefer);
before.next = after;
after.seq = before.seq + 1;
if(before.v != after.exp)
{
after.v = before.v;
}
head[i] = after;
}
boolean rc = false;
if(before.v == announce[i].exp)
{
announce[i].v = replace;
rc = true;
}else
{
announce[i].v = before.v;
}
head[i] = announce[i];
return rc;
}
}
80. 如果每个线程首先尝试加入自己的结点,在announce[i].seq !=0 后就推出apply()方法,仍然会出现有的线程一直成功而有的线程饿死的状况。如果线程成功的加入自己的结点后开始尝试帮助其它的线程,例如将While()中的帮助循环n-1次,应该是可以的。
81. 将max方法改成下面这样,然后用max(tail)来得到head[i]
package p79;
public class Node
{
public Node next;
public int seq;
public int exp;
public int v;
public Consensus<Node> decideNext;
public Node()
{
this.seq = 0;
this.next = null;
decideNext = new Consensus<Node>();
}
public Node(int exp, int replace)
{
this();
this.exp = exp;
this.v = replace;
}
public static Node max(Node tail)
{
Node pre = tail;
while(pre.next != null)
{
pre = pre.next;
}
return pre;
}
}
82.
1. 因为有可能节点 seq已经设置,但是节点没有放到head[]中,
所以实际上的start(A) = m + 1,但是head[A].seq用Node.max()计算出来为m,
使6.4.4不成立。
2.仍然能够正常工作。
因为这种情况下有(head[A].seq - start(A)) >= -1。
假设(head[A].seq - start(A)) < -1,比如 = -2;则必然在head[A]后至少有2个节点增加到链表中。假设他们的seq为m + 1, m + 2。
如果设置 m +2的线程从 head[]中得到m + 1的值,则这个不等式成立;
如果从链表中得到m + 1的值,则这个线程必然已经在while()循环中完成了after.seq = m + 1,head[i] = after的一轮操作,即m + 1已经被这个线程加到head中。
所以这个不等式总是成立。
根据这个不等式,和Theorem 6.4.1的推导方法,线程经过了最多(n + 1)次迭代后仍然能将自己的announce设置到head中。
83.
利用新添加的before和新添加的calculated flag。和java 垃圾搜集。tail的calculated初始化为true。当用before.next = after构建链表时,也用 after.before = before构建双向链表。
完成将announce添加到链表的操作后,计算log值时,不从tail开始,而是从current向before利用反向链表,直到找到一个before.calculated = true的node,作为初始值开始计算本线程的local log。
当head[i]的值计算完毕,calculated设置完毕后head[i].before = null;使节点脱离反向链表。
但是如果一个线程中途退出了,它将由head[i] hold住无限长的 next链表。所以其他线程也要帮助它脱离next链表。
准备一个全局的dummy node,它将作为next的填充物,防止before.next.decide在脱链后又被加入新的节点。
每个线程在计算完自己的log后帮助其他线程的过时head脱链:
检查每个head[j].seq。如果(current.seq - head[j].seq) >( n + 1);假设head[j].seq = m,则必然已经有一个node.seq = (m + 1)的calculated = true。即这个node已经不需要了。所以将head[j].next = dummy。
但是其他的线程在沿着链表遍历的时候可能会遇到这个节点。如果使用的是这个节点本身,或者它的before/next节点值,线程都能够发现这个节点的异常,比如after = before.next.decide() ; after == dummy。线程可以重新计算max重来。
因为这个处理并不影响线程的seq关系,所以无等待没有影响。
这个过时的节点可能会被附上新的before节点,但是当before关系被用到时,这个节点自身的calculated已经被置位,所以不会有被反向链表用到的时候。而新被链入的before节点在这个过时节点本身从next链中摘除并回收的时候,新加入的before节点也会被解除被引用的关系。
84.