158.
public class StripedHashSet extends BaseHashSet
{
final ReadWriteLock[] locks;
//...
public final void acquire(T x)
{
locks[x.hashCode() % locks.length].readLock().lock();
}
public void release(T x)
{
locks[x.hashCode() % locks.length].readLock().unlock();
}
public void resize()
{
for(Lock lock : locks)
{
lock.writeLock().lock();
}
//...
for(Lock lock: locks)
{
lock.writeLock().unlock();
}
}
}
159.
因为bucket要为hashset提供shortcut,但是对list的remove和对bucket的值remove不能原子的实现,所以会有问题。
如果先从list中删除,再处理bucket指向,则通过bucket访问hashset的线程有可能得到已经被删除的节点;
如果先处理bucket指向,再处理list,则考虑这样的case:
bucket = a, 在list中 a-->b;
线程A remove a,首先找到b,并认为b合法,应该有bucket=b;
线程B remove b,没有corner case, remove b结束;
线程A 令bucket=b, 在list中remove a,结束。
160.
摊还分析。
161.
如果允许一个比较大的但是flexible的数据结构,可以用14章介绍的SkipList。
如果要求任意大小,因为resize只考虑增加不考虑减少,所以可以采用这样的二叉树:
package p161;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicReference;
//First bit of key(BitSet) set as 1 to calculate length of bitset
public class Node
{
private final int key;
private final BitSet bits;
private AtomicReference> left;
private AtomicReference> right;
private AtomicReference sentinal;
private void init()
{
left = new AtomicReference>(null);
right = new AtomicReference>(null);
sentinal = new AtomicReference(null);
}
private Node(int key, BitSet bits)
{
this.key = key;
this.bits = bits;
init();
}
public Node()
{
this.key = 0;
this.bits = new BitSet(1);
this.bits.set(0, true);
init();
}
private Node createNode(boolean rChild)
{
int bitLen = bits.length();
int newValue = rChild? 1: 0;
BitSet newBits = new BitSet(bitLen + 1);
int i = 0;
for(; i < bitLen - 1; i ++)
{
newBits.set(i, bits.get(i));
}
newBits.set(i, rChild);
newBits.set(i + 1, true);
int newKey = key | (newValue << (bitLen - 1));
return new Node(newKey, newBits);
}
public Node getChild(boolean isRight)
{
AtomicReference> child = isRight? right: left;
if(child.get() != null)
{
return child.get();
}else
{
Node newChild = createNode(isRight);
child.compareAndSet(null, newChild);
return child.get();
}
}
public static BitSet IntToBits(int key)
{
long MASK = Long.MAX_VALUE;
int BITMASK = 1;
int index = 0;
//64bits enough for int
BitSet bits = new BitSet();
while((key & MASK) != 0)
{
boolean keyBit = (key & BITMASK) == 0? false: true;
bits.set(index, keyBit);
MASK <<= 1;
BITMASK <<= 1;
index ++;
}
bits.set(index, true);
return bits;
}
public static int BitsToInt(BitSet bits)
{
int key = 0;
for(int i = 0; i < bits.length() - 1; i ++)
{
int bitV = bits.get(i)? 1: 0;
key |= (bitV << i);
}
return key;
}
public Node getLeft()
{
return getChild(false);
}
public Node getRight()
{
return getChild(true);
}
public Node getLeftChild()
{
return left.get();
}
public Node getRightChild()
{
return right.get();
}
public int getKey()
{
return key;
}
public T getSentinal()
{
return sentinal.get();
}
public void setSentinal(T x)
{
sentinal.set(x);;
}
public BitSet getKeySet()
{
return bits;
}
}
package p161;
import java.util.BitSet;
public class LocklessBinaryTree
{
private Node root;
public LocklessBinaryTree(T initV)
{
root = new Node();
root.setSentinal(initV);
}
public Node getRoot()
{
return root;
}
public Node getNearestAncestor(BitSet key)
{
int len = key.length();
Node node = root;
Node child = node;
for(int i = 0; i < len - 1; i ++)
{
if(key.get(i))
{
child = node.getRight();
}else
{
child = node.getLeft();
}
//Make sure parent has sentinal set
if(child == null || child.getSentinal() == null)
{
return node;
}else
{
node = child;
}
}
return node;
}
public Node getChild(Node parent, boolean isRight)
{
if(parent == null)
{
return null;
}
Node node = parent.getChild(isRight);
return node;
}
}
package p161;
/*
* LockFreeHashSet.java
*
* Created on December 30, 2005, 12:48 AM
*
* From "Multiprocessor Synchronization and Concurrent Data Structures",
* by Maurice Herlihy and Nir Shavit.
* Copyright 2006 Elsevier Inc. All rights reserved.
*/
//import ch13.Hash.src.hash.*;
import java.util.BitSet;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @param T item type
* @author Maurice Herlihy
*/
public class LockFreeHashSet {
protected LocklessBinaryTree > tree;
protected AtomicInteger bucketSize;
protected AtomicInteger setSize;
private static final double THRESHOLD = 4.0;
public LockFreeHashSet()
{
BucketList list = new BucketList();
tree = new LocklessBinaryTree >(list);
bucketSize = new AtomicInteger(2);
setSize = new AtomicInteger(0);
}
public boolean add(T x) {
int myBucket = Math.abs(BucketList.hashCode(x) % bucketSize.get());
BucketList b = getBucketList(myBucket);
if (!b.add(x))
return false;
int setSizeNow = setSize.getAndIncrement();
int bucketSizeNow = bucketSize.get();
if (setSizeNow / (double)bucketSizeNow > THRESHOLD)
bucketSize.compareAndSet(bucketSizeNow, 2 * bucketSizeNow);
return true;
}
public boolean remove(T x) {
int myBucket = Math.abs(BucketList.hashCode(x) % bucketSize.get());
BucketList b = getBucketList(myBucket);
if (!b.remove(x)) {
return false; // she's not there
}
return true;
}
public boolean contains(T x) {
int myBucket = Math.abs(BucketList.hashCode(x) % bucketSize.get());
BucketList b = getBucketList(myBucket);
return b.contains(x);
}
private BucketList getBucketList(int myBucket)
{
BitSet bits = Node.IntToBits(myBucket);
Node> parent = tree.getNearestAncestor(bits);
if(parent.getKey() == myBucket)
{
return parent.getSentinal();
}
BitSet parentBits = parent.getKeySet();
int index = parentBits.length() - 1;
Node> node = null;
for(; index < (bits.length() - 1); index ++)
{
node = tree.getChild(parent, bits.get(index));
if(node.getSentinal() == null)
{
BucketList b = parent.getSentinal().getSentinel(node.getKey());
if(b != null) //Is that possible?
{
node.setSentinal(b);
}
}
parent = node;
}
return node.getSentinal();
}
}
162.