47.
如果n个线程刚好可以分成2组, 第一组的行为i与lemma5.1的行为一样,第二组的行为与Lemma5.1的行为一样,则它们有一个2值初始状态。48.
49.
因为critical state必须是bivalent,所以2个后续只能 1-valent + 0-valent或者 x + bivalent。因为if any thread moves ,the protocol has a critical state,所以后续只能是univalent。所以只能是 1-valent + 0-valent。
50.
将 n个线程分成2组,每组的线程都完全同步并行,并且行为与2线程protocol的2个线程完全一样;则如果2个线程不能decide,n个线程也不能。51.
将k值与2值做映射,比如
if(k >= MAXK / 2)
{
k = 1;
}else
{
k = 0;
}
则n线程decide on k后再映射成2值。
52.
以58题答案为例
53.
consensus >= 2:
package jokes;
import java.util.Stack;
public class StackProtocol
{
private int[] proposes;
static ThreadLocal<Integer> ThreadId = new ThreadLocal<Integer>()
{
protected Integer initialValue()
{
return new Integer(0);
}
};
static final int WIN = 1;
static final int LOSE = 2;
private Stack<Integer> stack;
public StackProtocol()
{
proposes = new int[2];
stack = new Stack<Integer>();
stack.push(LOSE);
stack.push(WIN);
}
private void propose(int v)
{
proposes[ThreadId.get()] = v;
}
public int decide(int v)
{
propose(v);
if(stack.pop() == WIN)
{
return proposes[ThreadId.get()];
}else
{
return proposes[1 - ThreadId.get()];
}
}
}
consensus < 3:
Both A & B pop(): 不论A先或B先,2个pop()结束后,C solo的时候无法区分谁是winner
A push & B pop: A.push() --> B.pop() 与AB都没有动作不能区分; B.pop() --> A.push() 与只有A.push()无法区分。
A push & B push: 2种顺序Stack状态一样,无法区分。
54.
如下所示:
package jokes;
import java.util.concurrent.LinkedTransferQueue;
public class QueuePeekConsensus
{
private LinkedTransferQueue<Integer> q;
public QueuePeekConsensus(int n)
{
q = new LinkedTransferQueue<Integer>();
}
private void propose(int v)
{
q.put(v);
}
public int decide(int v)
{
propose(v);
return q.peek();
}
}
55.
不能。因为R(AB)只能被A和B访问,即只有2个现成参与。可以将CompareAndSet用getAndIncrement实现。则这个协议是由Common2寄存器和原子寄存器构成的,一致数为2。
一个反例:
A.CAS(RAB) --> B.CAS(RAB) --> B.CAS(RBC) --> C.CAS(RBC) --> C.CAS(RAB) --> A.CAS(RBC)
==> A-->B-->C-->A
56.
如下所示:
package jokes;
import java.util.concurrent.atomic.*;
public class CAS32Protocol
{
public final int THREADNUM = 3;
public final int THREADA = 0;
public final int THREADB = 1;
public final int THREADC = 2;
public final int INITIAL = -1;
private AtomicInteger[] casObjs;
private int[] proposes;
public CAS32Protocol()
{
proposes = new int[THREADNUM];
casObjs = new AtomicInteger[THREADNUM]; //ab, bc, ac
for(int i = 0; i < THREADNUM; i ++)
{
casObjs[i] = new AtomicInteger(INITIAL);
}
}
private synchronized boolean propose(int index, int exp1, int exp2, int v1, int v2)
{
int a = index;
int c = (index + 2) % THREADNUM;
if(casObjs[a].compareAndSet(exp1, v1)
&& casObjs[c].compareAndSet(exp2, v2))
{
return true;
}
return false;
}
//It dumps when champion and runner-up both halted
public int decide(int threadId, int v)
{
int a = threadId;
int b = (threadId + 1) % 3;
int c = (threadId + 2) % 3;
proposes[a] = v;
if(propose(a, INITIAL, INITIAL, THREADA, THREADA))
{
proposes[b] = proposes[c] = proposes[a];
//A wins
}else if(propose(a, THREADB, INITIAL, THREADB, THREADA))
{
// B-->A-->C
proposes[a] = proposes[b];
}else if(propose(a, INITIAL, THREADC, THREADA, THREADC))
{
//C-->A-->B
proposes[a] = proposes[c];
}else
{
//Waiting for winner
while(proposes[b] != proposes[c]);
proposes[a] = proposes[b];
}
return proposes[a];
}
}
57.
只要能够在loser知道自己是loser之前就能获得announce的值,就有infinite consensus58.
1)如下所示:
package jokes;
import java.util.concurrent.atomic.*;
public class Sticky
{
public enum StickyValueEnum
{
INIT(-1),
ZERO(0),
ONE(1);
private int v;
private StickyValueEnum(int v)
{
this.v = v;
}
public int get()
{
return v;
}
}
private AtomicInteger v;
public Sticky()
{
v = new AtomicInteger(StickyValueEnum.INIT.get());
}
public boolean write(StickyValueEnum v)
{
return this.v.compareAndSet(StickyValueEnum.INIT.get(), v.get());
}
public int read()
{
return v.get();
}
}
package jokes;
import jokes.Sticky;
import jokes.Sticky.StickyValueEnum;
public class BinarySticky
{
private Sticky sticky;
public BinarySticky()
{
sticky = new Sticky();
}
public int decide(StickyValueEnum v)
{
sticky.write(v);
return sticky.read();
}
}
2)
package jokes;
import jokes.Sticky;
import jokes.Sticky.StickyValueEnum;
//import java.nio.ByteBuffer;
import java.util.concurrent.atomic.*;
import java.util.BitSet;
public class MValentSticky
{
private static ThreadLocal<Integer> ThreadId = new ThreadLocal<Integer>()
{
protected Integer initialValue()
{
return new Integer(0);
}
};
private AtomicInteger[] proposes;
private Sticky[] stickys;
private final int stickyNum;
private final int threadNum;
public MValentSticky(int vNum, int threadNum)
{
this.threadNum = threadNum;
proposes = new AtomicInteger[this.threadNum];
for(int i = 0; i < threadNum; i ++)
{
proposes[i] = new AtomicInteger(Sticky.StickyValueEnum.INIT.get());
}
stickyNum = (int)Math.ceil(Math.log(vNum) / Math.log(2));
stickys = new Sticky[stickyNum];
for(int i = 0; i < stickyNum; i ++)
{
stickys[i] = new Sticky();
}
}
private static StickyValueEnum GetStickyValue(boolean bit)
{
if(bit)
{
return StickyValueEnum.ONE;
}else
{
return StickyValueEnum.ZERO;
}
}
private BitSet getBitSet(int v)
{
BitSet bits = new BitSet(stickyNum);
int i = 0;
while(v != 0)
{
if(v % 2 == 1)
{
bits.set(i);
}
i ++;
v >>= 1;
}
return bits;
}
//maxBitIndex: first bit not matched
private BitSet foundMatchWinner(BitSet me, int maxBitIndex)
{
if(maxBitIndex >= stickyNum)
{
return me;
}
//In same order, losers may find same winner
//While it is not important
for(int i = 0; i < threadNum; i ++)
{
if(i == ThreadId.get())
{
continue;
}
BitSet other = getBitSet(proposes[i].get());
int j = 0;
while(j < maxBitIndex && me.get(j) == other.get(j));
if(j >= maxBitIndex)
{
return other;
}
}
return me;
}
private int convert(BitSet bits)
{
int v = 0;
for(int i = bits.length(); i >= 0; i --)
{
v <<= 1;
if(bits.get(i))
{
v |= 1;
}
}
return v;
}
public int decide(int proposeV)
{
int v = proposeV;
int index = ThreadId.get();
proposes[index].set(v);
BitSet bits = getBitSet(v);
for(int i = 0; i < stickyNum; i ++)
{
if((stickys[i].write(GetStickyValue(bits.get(i)))))
{
continue;
}else
{
bits = foundMatchWinner(bits, i);
//Not necessary to update proposes[]
//as candidates can get value from sticky
}
}
return convert(bits);
}
public static void main(String[] args)
{
MValentSticky toy = new MValentSticky(4, 2);
int rc = toy.decide(3);
System.out.println("End of test " + rc);
}
}
59.
一个用原子寄存器实现的SetAgree(2)如下所示。因为它可以用AtomicRegister实现,所以一致数为1。但是当 k < n (线程数), consensus number = infinite。
package jokes;
import java.util.concurrent.atomic.*;
public class SetAgree2
{
private AtomicBoolean[] proposes;
public final int ThreadNum;
public SetAgree2(int threadNum)
{
this.ThreadNum = threadNum;
proposes = new AtomicBoolean[this.ThreadNum];
for(int i = 0; i < this.ThreadNum; i ++)
{
proposes[i] = new AtomicBoolean();
}
}
public void propose(boolean v, int threadId)
{
if(threadId < ThreadNum && threadId >= 0)
{
proposes[threadId].set(v);
}
}
public boolean decide(int threadId)
{
if(threadId < ThreadNum && threadId >= 0)
{
return proposes[threadId].get();
}
return false;
}
}
60.
161
A类:不能。将收到消息看成对一个对象进行读操作的返回。因为这个对象之提供读和写操作,即相当于原子寄存器。则A类型的实现相当于对一组原子寄存器进行读写实现的,所以consensus number =1
B类:类似于访问一个提供peek方法的queue,consensus number = infinite
62.
如果A.propose == 1, A decides 1; B可以随意决定都是正确的。如果A.propose == 0 而且B还没有propose,则A可以决定一个合理的值,B 在决定的时候因为可以看到A,所以可以配合这个决定。
因为如果B后来propose了0则A必须决定0,所以这种情况下A决0。
如果A decides时候B已经有了propose,即B决定在A之前。如果B.propose == 0,则A必须决定0,否则A必须决定1,因为B已经决定了1。
package jokes;
public class QuasiConsensus
{
public static final int THREADNUM = 2;
int[] proposes;
public QuasiConsensus()
{
proposes = new int[THREADNUM];
for(int i = 0; i < THREADNUM;i ++)
{
proposes[i] = -1;
}
}
//threadId: A--> true, B-->false
public int decide(boolean threadId, int v)
{
if(threadId) // thread A
{
proposes[1] = v;
if(v == 1)
{
return 1;
}else
{
if(proposes[0] != 1)
{
return 0;
}else
{
return 1;
}
}
}
else // thread B
{
proposes[0] = v;
if(v == 0)
{
return 0;
}else
{
if(proposes[1] != 0)
{
return 1;
}else
{
return 0;
}
}
}
}
}
63.
因为一致对象已经有决定的能力,即只要输入状态时合法的,一致性就是可能的。64.
同48题?65.
package jokes;
import java.util.concurrent.atomic.*;
import jokes.NoDefineException;
public class TeamConsensusObject
{
private AtomicInteger[] proposes;
public final static int INIT = -1;
public TeamConsensusObject()
{
proposes = new AtomicInteger[2];
for(int i = 0; i < proposes.length; i ++)
{
proposes[i] = new AtomicInteger(INIT);
}
}
public void propose(int v)
{
if(!proposes[0].compareAndSet(INIT, v))
{
proposes[1].compareAndSet(INIT, v);
}
}
public int decide(int v) throws NoDefineException
{
if(v == proposes[0].get())
{
return v;
}else if(v == proposes[1].get())
{
return proposes[0].get();
}else
{
throw new NoDefineException("");
}
}
}
package jokes;
import jokes.NoDefineException;
import jokes.TeamConsensusObject;
public class TeamConsensusProtocol
{
private TeamConsensusObject[] nodes;
private int threadNum;
public TeamConsensusProtocol(int threadNum)
{
this.threadNum = (int) Math.pow(2, (Math.ceil((Math.log(threadNum)/ Math.log(2)))));
System.out.println("Get threadNum " + this.threadNum);
nodes = new TeamConsensusObject[this.threadNum * 2];
for(int i = 0; i < nodes.length; i ++)
{
nodes[i] = new TeamConsensusObject();
}
}
public int decide(int threadId, int proposeV) throws NoDefineException
{
if(threadId >= this.threadNum || threadId < 0)
{
throw new NoDefineException("");
}
int nodeId = threadId;
int v = proposeV;
int step = this.threadNum;
int base = 0;
while(step > 0)
{
nodes[nodeId].propose(v);
v = nodes[nodeId].decide(v);
threadId = (threadId + 1) / 2;
base += step;
nodeId = base + threadId;
step /= 2;
}
return v;
}
public static void main(String[] args)
{
TeamConsensusProtocol tester = new TeamConsensusProtocol(7);
try
{
System.out.println("Decide " + tester.decide(6, 5));
}catch(Exception e)
{
e.printStackTrace();
}
}
}
66.
58题解法,将值换成thread id67.
如果一个算法的执行的步数是有限的,则无锁和无等待是等价的。对于一个给定的一致性对象,既然它的decide()方法只被每个线程执行一次,则它的无锁实现也是无等待的,反之亦然。所以没有原子寄存器的无等待实现也就没有它的无锁实现。
68.
只用原子寄存器实现的scan实现的peek()操作的一致数为1。一旦有deq被调用,peek()返回的在deq调用前和调用后的得到的结果是不一样的,不满足一致的条件。如果没有线程调用dep,因为enq不是一个原子操作,虽然getAndIncrement()的一致数为2,但是items[slot]=x是一个原子寄存器的操作,一致数只有1。所以即使不用deq实现的protocol一致数也是1。
69.
package jokes;
import java.util.concurrent.atomic.*;
public class NewCASObj
{
private AtomicInteger v;
public NewCASObj(int v)
{
this.v = new AtomicInteger(v);
}
public int newCompareAndSet(int exp, int replace)
{
v.compareAndSet(exp, replace);
do
{
exp = v.get();
}while(!v.compareAndSet(exp, exp));
return exp;
}
}
70.
根据5.8.1.的证明,n界CAS的一致数>=n。如果有n+1线程第n+1次调用这个对象,返回值为⊥,则前n次调用的任何状态都不能被区分,n+k (k > 0)次调用都不能得到winner的信息。
71.
package jokes;
import java.util.concurrent.atomic.*;
public class CASAssign32
{
AtomicInteger[] rs;
public CASAssign32(int initValue)
{
rs = new AtomicInteger[3];
for(int i = 0; i < rs.length; i ++)
{
rs[i] = new AtomicInteger(initValue);
}
}
//Assign in by increasing index
public void assign(int i0, int i1, int v0, int v1)
{
if(i0 >= 3 || i1 >= 3 || i0 < 0 || i1 < 0 || i0 == i1)
{
return;
}
if(i0 > i1)
{
int tmp = i0;
i0 = i1;
i1 = tmp;
}
while(true)
{
int origV0 = rs[i0].get();
int origV1 = rs[i1].get();
if(rs[i0].compareAndSet(origV0, v0))
{
if(rs[i1].compareAndSet(origV1, v1))
{
return;
}
}
}
}
/*
* Check by decreasing index.
* For example, when rs2 modified in read rs0 in assign(1, 2, x, x)
* The returned value is valid as r1, r2 had been checked
*/
public int read(int i) throws NoDefineException
{
if(i >= 3 || i < 0)
{
throw new NoDefineException("");
}
while(true)
{
int v0 = rs[0].get();
int v1 = rs[1].get();
int v2 = rs[2].get();
if(rs[2].compareAndSet(v2, v2))
if(rs[1].compareAndSet(v1, v1))
if(rs[0].compareAndSet(v0, v0))
{
return rs[i].get();
}
}
}
}
72.
73.
package jokes;
import java.util.concurrent.atomic.*;
public class CASConsensus
{
private AtomicInteger proposeId;
private int[] proposes;
private int threadNum;
public CASConsensus(int threadNum)
{
this.threadNum = threadNum;
proposes = new int[this.threadNum];
proposeId = new AtomicInteger(-1);
}
public int decide(int threadId, int v)
{
proposes[threadId] = v;
if(proposeId.compareAndSet(-1, threadId))
{
return v;
}else
{
for(int i = 0; i < threadNum; i ++)
{
//Suppose 0 is invalid/initial value
if(proposes[i] != 0)
{
if(proposeId.compareAndSet(i, i))
{
return proposes[i];
}
}
}
}
//Make compiler happy
return v;
}
}