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.