163.
每个节点有相同的高度,并且是一次失败的查找。
164.
N * pow(p, n)
165.
public final class LazySkipList
{
static final int MAX_LEVEL = ...;
int[] levelCounters;
volatile int highestLevel;
Lock levelCounterLock;
...
public LazySkipList()
{
...
levelCounters = new int[MAX_LEVEL + 1];
levelCounterLock = new ReentrantLock();
highestLevel = 0;
}
private int updateHighestLevel(boolean addAction, int updatedLevel)
{
levelCounterLock.lock();
try
{
if(addAction)
{
levelCounters[updatedLevel] ++;
if(updatedLevel > highestLevel)
{
highestLevel = updatedLevel;
}
}else
{
levelCounters[updatedLevel] --;
if(updatedLevel == highestLevel
&& levelCounters[updatedLevel] == 0)
{
for(int i = highestLevel - 1; i >= 0; i --)
{
if(levelCounters[i] > 0)
{
highestLevel = i;
break;
}
}
}
}
}finally
{
levelCounterLock.unlock();
}
}
int find(T x, Node
[] preds, Node
[] succs)
{
...
//No error if highestLevel is being upated.
//If highestLevel raised, current highestLevel can cover the subset
//If highestLevel lowered, find() reaches TAIL on upper layers
for(int level = highestLevel; level >= 0; level --)
{
...
}
...
}
boolean add(T x)
{
...
newNode.fullyLinked = true;
updateHighestLevel(true, topLevel);
return true;
...
}
boolean remove(T x)
{
...
victim.lock.unlock();
updateHighestLevel(false, victim.topLevel);
return true;
...
}
}
166.
key可以不唯一,item唯一,因为add/remove/find都是以x(item)为参数。
所以find方法改为找到该节点 或者 找到该key值的末尾。
add方法也不用改变,因为add是从0层开始链入;如果2个add试图以同一个节点为尾节点加入新节点,则肯定有一个add会抢锁失败然后valid失败;如果以不同的节点为尾节点,表示某一个尾节点的next已经被改变,valid也会失败。如果一个节点链入了0层,则另一个的find会失败。
int find(T x, Node
[] preds, Node
[] succs)
{
int key = x.hashCode();
int lFound = -1;
Node
pred = head;
for(int level = MAX_LEVEL; level >= 0; level --)
{
Node
curr = pred.next[level];
while(key > curr.key || (curr.key == key && curr.item != x))
{
pred = curr;
curr = curr.next[level];
}
if(lFound == -1 && key == curr.key && curr.item == x)
{
lFound = level;
}
preds[level] = pred;
succs[level] = curr;
}
return lFound;
}
167.
这个方法不成功的可线性化点在 find返回 false。
这仍然是一个无锁算法。根据无锁的定义:it guarantees that infinitely often some method call finishes in a finite number of steps. 即无数次的方法调用中总有在有限步内完成的。如果无数次的方法调用都没有能够返回的调用,对应的case是 find总能找到并行add的可删除节点,但是总是标记失败且发现被删除节点已经被标记。即失败的调用者总是对应一个成功的将被删除节点标记并返回的调用。符合lockless的调用。
168.
这只是一种临时状态。
169.
public boolean find(T x, Node
[] preds, Node
[] succs)
{
int bottomLevel = 0;
int key = x.hashCode();
boolean[] marked = {false};
boolean snip;
Node
pred = null, curr = null, succ = null;
Node
origCurr = null;
retry:
while(true)
{
pred = head;
for(int level = MAX_LEVEL; level >= bottomLevel; level --)
{
curr = pred.next[level].getReference();
while(true)
{
origCurr = curr;
succ = curr.next[level].get(marked);
while(marked[0])
{
curr = succ;
succ = curr.next[level].get(marked);
}
snip = pred.next[level].compareAndSet(origCurr, curr, false, false);
if(!snip)
{
continue retry;
}
if(curr.key < key)
{
pred = curr;
curr = succ;
}else
{
break;
}
}
preds[level] = pred;
succs[level] = curr;
}
return (curr.key == key);
}
}
求例子 。
170.
Add: 如图所示,find(x)将会得到succ = null;
Remove: 如图所示,如果这时候调用contain,将得到错误的结果;或者任何值 > 2的update操作,都会在find的时候在
retry --> sip = pred.next[level].compareAndSet(curr, succ, false, false) 死循环,因为2.layer1.marked = true。
171.
172.