Art of Multiprocessor Programming 答案 ch15

本文探讨了SimpleTree在多处理器编程中的静态一致性性质,分析了如何处理超过预设阈值的情况,以及如何在无锁编程中利用AtomicBoolean确保线性化。还提到了线程间的冲突点,例如在removeMin和add操作中的潜在问题,以及全局锁中创建对象的开销。重点讨论了静态一致性的概念及其在实现中的应用。

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

173. SimpleTree 是不可线性化但是静态一致的。


174. 思路是

如果不要求保存状态可重用,则可以在输出端连接H个开关,比如说,如果output=1已经有token经过了,则当前counter>H,返回H。

如果考虑可重用,则发现>H则再加入一个反令牌,<0则再加入一个令牌。一个问题是如何在decrement用反令牌实现的前提下用开关标记是否是超过H的。

因为衍射树是静态一致的,AtomicBoolean也是静态一致的,他们的组合也是静态一致的。

package p174;

public class Balancer {
	  Boolean toggle;
	  
	  public Balancer() 
	  {
	    toggle = true;
	  }
	  
	  public synchronized int traverse(boolean token) 
	  {
		  if(token)
		  {
			  try 
			  {
				  if (toggle) 
				  {
					  return 0;
				  } else 
				  {
					  return 1;
				  }
			  } finally 
			  {
				  toggle = !toggle;
			  }
		  }else
		  {
			  toggle = !toggle;
			  if(toggle)
			  {
				  return 0;
			  }else
			  {
				  return 1;
			  }
		  }
	  }
}
	  



package p174;

public class DiffractingTree
{
	Balancer root;
	DiffractingTree[] child;
	int size;
	
	public DiffractingTree(int mySize)
	{
		size = mySize;
		root = new Balancer();
		if(size > 2)
		{
			child = new DiffractingTree[]
				{
					new DiffractingTree(size / 2),
					new DiffractingTree(size / 2),
				};
		}
	}
	
	public int traverse(boolean token)
	{
		int half = root.traverse(token);
		
		if(size > 2)
		{
			return (2 * (child[half].traverse(token)) + half);
		}else
		{
			return half;
		}
	}
}


package p174;

import java.util.concurrent.atomic.AtomicBoolean;

public class Counter 
{
	private final int bound;
	private DiffractingTree tree;
	private AtomicBoolean flags[];
	
	public Counter(int bound)
	{
		this.bound = bound;
		tree = new DiffractingTree(bound);
		flags = new AtomicBoolean[bound];
		
		for(int i = 0; i < flags.length; i ++)
		{
			flags[i] = new AtomicBoolean(false);
		}
	}
	
	public int boundedGetAndIncrement()
	{
		int tmpRc = tree.traverse(true);
		if(flags[tmpRc].compareAndSet(false, true))
		{
			return tmpRc;
		}else
		{
			tmpRc = tree.traverse(false);
			flags[tmpRc].compareAndSet(true, false);
			return bound;
		}
	}
	
	public int boundedGetAndDecrement()
	{
		int tmpRc = tree.traverse(false);
		
		if(flags[tmpRc].compareAndSet(true, false))
		{
			return tmpRc;
		}else
		{
			tmpRc = tree.traverse(true);
			flags[tmpRc].compareAndSet(false, true);
			return 0;
		}
	}
	
	public static void main(String[] args)
	{
		Counter counter = new Counter(4);
		
		for(int i = 0; i < 2; i ++)
		{
			int rc = counter.boundedGetAndIncrement();
			System.out.println("" + i + "---------> " + rc);
		}
		
		for(int i = 0; i < 9; i ++)
		{
			int rc = counter.boundedGetAndDecrement();
			System.out.println("-" + i + "---------> " + rc);
		}
	}
}


175.

负数,非零,走到错误的叶子。


176. 看不懂题目。这个本来就是有界容量的。


177.

removeMin超过add,在内部节点中发现无路可走。


178.


179.

import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Heap with fine-grained locking and arbitrary priorities.
 * @param T type manged by heap
 * @author mph
 */
public class FineGrainedHeap implements PQueue {

  private static int ROOT = 1;
  private static int NO_ONE = -1;
  private Lock heapLock;
  int next;
//  HeapNode[] heap;
  ArrayList> heap;

  /**
   * Constructor
   * @param capacity maximum number of items heap can hold
   */
  public FineGrainedHeap(int capacity) {
    heapLock = new ReentrantLock();
    next = ROOT;
    
    heap = new ArrayList>(capacity + 1);

  }

  /**
   * Add item to heap.
   * @param item Uninterpreted item.
   * @param priority item priority
   */
  public void add(T item, int priority) {
    heapLock.lock();
    int child = next++;
    
    if(heap.size() <= child)
    {
    	HeapNode childElem = new HeapNode ();
    	heap.add(childElem);
    }
    
    //It should be right as next ++, add new element are sequential as they were locked by helpLock.
    //And there is no removal operation from list.
    //Anyway, the get(child) can throw Exception when index and element really mismatched.
    heap.get(child).lock();
    heapLock.unlock();
    //...
  }
  
  //...
}

临时创建对象的开销,尤其是在全局锁中创建对象的开销。


180.

好处在于如果没有这个特性,相邻的2个插入在时间上是相差最短的,而且最容易有相同的父节点,所以产生同步开销的机率最大。


以 reverseIncrement为例:



public int reversedIncrement()
{
    //如果counter越界,则归零
    if(count ++ == 0)
    {
        reverse = highBit = 1;
        return reverse;
    }
    
    //取bit为除了最高位的第一位。即根结点下的第一层子树位。
    int bit = highBit >> 1;
    
    while(bit != 0)
    {
        //从高位开始异或,所以相邻的2个节点必然不在同一个子树上。
        reverse ^= bit;
        
        /*
        * 因为reverse ^= bit,又(reverse & bit == 0) 
        * ==> 假设bit = 0100,则异或之前reverse = x0xx
        * ==> 原来的reverse在左树上,则找到右子树上的对应节点,退出。
        * ==> 如果原来的reverse在右树上,则异或后转到左子树
        * ==> 且bit >>= 1,即到下一层子树继续这个过程,直到找到一个右子树上的点。
        * 
        * ==> 以上图为例,count = 8对应的reverse为1000,为子树1的最左节点
        * ==> count = 9时,因为8在左子树,找到子树1的右子树的对应节点为12
        * ==> count = 10,第一轮找到对称节点为8,在左子树,
        *   ==> 下降一层,到子树2的右子树上找对应的节点,找到10
        * ==> count = 11,找到1的右子树的对应节点14
        * ==> count = 12,第一轮得到对应节点为10,在左子树,
        *   ==> 下降一层,在子树2上,得到对应节点为8,得知原节点在2的右子树上,
        *       ==> 下降一层,在子树4上,得到对应右节点为9
        * ==> ...
        * ==> count = 15,得到节点15
        * ==> count = 16,
        */
        if((reverse & bit) != 0)
        {
            break;
        }
        bit >>= 1;
    }
    
    /*
    * count = 16,得到bit = 0,即叶子节点层已经没有空余节点了
    * 将最高位左移一位,即树高加一层,并在新层上找next。
    */
    if(bit == 0)
    {
        reverse = highBit << = 1;
    }
    
    return reverse;
}

181.

作者有给出


182.

不从头开始也是可以的,因为要求只是静态一致性。

package p182;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;



public class LazySkipListQueue
{
	static final int MAX_LEVEL = 4;
	static int randomSeed = (int)(System.currentTimeMillis()) | 0x0100;
	
	private static int randomLevel() {
		int x = randomSeed;
		x ^= x << 13;
		x ^= x >>> 17;
		randomSeed = x ^= x << 5;
		if ((x & 0x80000001) != 0) // test highest and lowest bits
			return 0;
		int level = 1;
		while (((x >>>= 1) & 1) != 0) ++level;
		return Math.min(level, MAX_LEVEL-1);
	}
	private static final class Node
	{
		final Lock lock  = new ReentrantLock();
		final T item;
		final int priority;
		final Node[] next;
		volatile boolean marked = false;
		volatile boolean fullLinked = false;
		private int topLevel;
		@SuppressWarnings("unchecked")
		public Node(int priority)
		{
			this.item = null;
			this.priority = priority;
			next = (Node[])new Node[MAX_LEVEL + 1];
			topLevel = MAX_LEVEL;
		}
		
		@SuppressWarnings("unchecked")
		public Node(T x, int priority)
		{
			this.item = x;
			this.priority = priority;
			int height = randomLevel();
			next =  (Node[])new Node[height + 1];
			topLevel = height;
		}
		
		public void lock()
		{
			lock.lock();
		}
		
		public void unlock()
		{
			lock.unlock();
		}
	}
	
	final Node head = new Node(Integer.MIN_VALUE);
	final Node tail = new Node(Integer.MAX_VALUE);
	
	public LazySkipListQueue()
	{
		for(int i = 0; i < head.next.length; i ++)
		{
			head.next[i] = tail;
		}
	}
	
	int find(Node node, Node[] preds, Node[] succs)
	{
		int lFound = -1;
		Node pred = head;
		for(int level = MAX_LEVEL; level >= 0; level --)
		{
			Node curr = pred.next[level];
			while(node.priority > curr.priority)
			{
				pred = curr;
				curr = pred.next[level];
			}
			if(lFound == -1 && node.priority == curr.priority)
			{
				lFound = level;
			}
			preds[level] = pred;
			succs[level] = curr;
		}
		
		return lFound;
	}
	
	@SuppressWarnings("unchecked")
	boolean add(Node newNode)
	{
		int topLevel = randomLevel();
		Node[] preds = (Node[]) new Node[MAX_LEVEL + 1];
		Node[] succs = (Node[]) new Node[MAX_LEVEL + 1];
		while(true)
		{
			int lFound = find(newNode, preds, succs);
			if(lFound != -1)
			{
				Node nodeFound = succs[lFound];
				if(!nodeFound.marked)
				{
					while(!nodeFound.fullLinked){}
					return false;
				}
				continue;
			}
			
			int highestLocked = -1;
			
			try
			{
				Node pred, succ;
				boolean valid = true;
				for(int level = 0; valid && (level < topLevel); level ++)
				{
					pred = preds[level];
					succ = succs[level];
					pred.lock();
					highestLocked = level;
					valid = !pred.marked && !succ.marked && pred.next[level] == succ;
				}
				if(!valid)
				{
					continue;
				}
				
				for(int level = 0; level <= topLevel; level ++)
				{
					newNode.next[level] = succs[level];
				}
				for(int level = 0; level <= topLevel; level ++)
				{
					preds[level].next[level] = newNode;
				}
				newNode.fullLinked = true;
				return true;
			}finally
			{
				for(int level = 0; level <= highestLocked; level ++)
				{
					preds[level].unlock();
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	private boolean remove(Node victim)
	{
		boolean isMarked = false;
		Node[] preds = (Node[]) new Node[MAX_LEVEL + 1];
		Node[] succs = (Node[]) new Node[MAX_LEVEL + 1];
		int topLevel = victim.topLevel;

		while(true)
		{
			int lFound = find(victim, preds, succs);
			
			if(!(lFound != -1 
					&& lFound == victim.topLevel
					&& victim.marked))
			{
				return true;
			}
			
			victim.lock();
			
			int highestLocked = -1;
			
			try
			{
				Node pred;
				boolean valid = true;
				
				for(int level = 0; valid && (level <= topLevel); level ++)
				{
					pred = preds[level];
					pred.lock();
					highestLocked = level;
					valid = !pred.marked && pred.next[level] == victim;
				}
				if(!valid)
				{
					continue;
				}
				for(int level = topLevel; level >= 0; level --)
				{
					preds[level].next[level] = victim.next[level];
				}
				victim.unlock();
				return true;
			}finally
			{
				for(int i = 0; i <= highestLocked; i ++)
				{
					preds[i].unlock();
				}				
			}
			
		}
	}

	
	public Node findAndMarkMin()
	{
		Node ret = null;
		Node pred = null;
		Node curr = null;
		
		while(true)
		{
			pred = head;
			curr = pred.next[0];
			
			while(curr.marked && curr != tail)
			{
				pred = curr;
				curr = pred.next[0];
			}
			
			if(curr == tail)
			{
				return null;
			}else
			{
				try
				{
					pred.lock();
					curr.lock();
					if(!curr.marked && !pred.marked && pred.next[0] == curr)
					{
						curr.marked = true;
						ret = curr;
						
						remove(ret);
						return ret;
					}
				}finally
				{
					pred.unlock();
					curr.unlock();
				}
			}
		}
		
	}
	
}

183.

2个线程同时findAndRemoveMin,因为都是从head开始沿list[bottom]寻找,所以很有可能在head.next[0]上冲突。


184.

因为有了全局同步的时间,所以任何相关事件都可以全排序,所以可以线性化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值