Art of Multiprocessor Programming 答案 ch13

本文详细介绍了 Lock-Free HashSet 的实现原理与优化策略,包括读写锁的使用、桶和链表的同步问题以及摊还分析。同时,讨论了灵活的数据结构 SkipList 和二叉树的使用场景。最后,通过代码示例展示了如何实现 Lock-Free HashSet 类,并解释了其工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值