Art of Multiprocessor Programming 答案 ch10

119.

  private enum NodeType {ITEM, RESERVATION, SENTINEL};

public class SynchronousDualQueue<T> {
  AtomicReference<Node> head;
  AtomicReference<Node> tail;  
  private final Node NullNode = new Node(null, NodeType.SENTINEL);
  
/* Replace all null with NullNode */
}

120.

package p120;

public class TwoThreadLockFreeQueue<T> 
{
	int head = 0, tail = 0;
	T[] items;
	
	@SuppressWarnings("unchecked")
	public TwoThreadLockFreeQueue(int capacity)
	{
		head = tail = 0;
		items = (T[]) new Object[capacity];
	}
	
	public void enq(T x)
	{
		//rmb
		while(tail - head == items.length) 
		{
			//rmb
		}
		
		items[tail % items.length] = x;
		tail ++;
		//wmb
	}
	
	public Object deq()
	{
		//rmb
		while(tail == head) 
		{
			//rmb 
		}
		
		Object x = items[head % items.length];
		head ++;
		//wmb
		return x;
	}
}

121.

package p121;

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

public class TwoLockArrayQueue
   
   
    
    
{
	private final int capacity;
	private int enqSideSize;
	private int deqSideSize;
	private Lock enqLock;
	private Condition enqCond;
	private Lock deqLock;
	private Condition deqCond;
	private int head;
	private int tail;
	private T[] datas;
	
	@SuppressWarnings("unchecked")
	public TwoLockArrayQueue(int cap)
	{
		capacity = cap;
		enqSideSize = 0;
		deqSideSize = 0;
		head = 0;
		tail = 0;
		enqLock = new ReentrantLock();
		deqLock = new ReentrantLock();
		enqCond = enqLock.newCondition();
		deqCond = deqLock.newCondition();
		
		datas = (T[]) new Object[capacity];
	}
	
	public void enq(T x)
	{
		boolean notifyDeq = false;
		
		enqLock.lock();
		try
		{
			while(enqSideSize == capacity)
			{
				deqLock.lock();
				try
				{
					enqSideSize = capacity - deqSideSize;
					deqSideSize = 0;
				}finally
				{
					deqLock.unlock();
				}
		
				if(enqSideSize == capacity)
				{
					enqCond.await();
				}
			}
			
			datas[tail % capacity] = x;
			tail ++;
			enqSideSize ++;
		
			//No notification lost as deqSideSize can only increase and still fit the condition
			if(enqSideSize - deqSideSize <= 1)
			{
				notifyDeq = true;
			}
			
		}catch(InterruptedException e)
		{
			e.printStackTrace();
		}finally
		{
			enqLock.unlock();
		}
		
		if(!notifyDeq) return;
		
		deqLock.lock();
		try
		{
			deqCond.signalAll();
		}finally
		{
			deqLock.unlock();
		}
	}
	
	public T deq()
	{
		T x = null;
		boolean notifyEnq = false;
		
		deqLock.lock();
		
		try
		{
			/*
			 * No notification lost as when enqSideSize increased in between while and await,
			 * there would always an enq thread knows enqSideSize - deqSideSize <= 1 
			 * and requires deqLock for notification. 
			 */
			while(deqSideSize >= enqSideSize)
			{
				deqCond.await();
			}
			
			x = datas[head % capacity];
			head ++;
			deqSideSize ++;

			if(enqSideSize >= capacity)
			{
				notifyEnq = true;
			}
		}catch(InterruptedException e)
		{
			e.printStackTrace();
		}finally
		{
			deqLock.unlock();
		}
		

		if(notifyEnq)
		{
			enqLock.lock();
			try
			{
				enqCond.signalAll();
			}finally
			{
				enqLock.unlock();
			}
		}
		return x;
	}
}

   
   

122. 必须。head.next != null --> deq 必须是原子的,否则可能有这样的case:queue中只有一个元素,2个线程deq,2个现成都得到head.next != null,然后2个现成分别在拿到锁之后deq,错误。

123.

使用SynchronousDualQueue, 在enq或deq消除(而不是加入list)的时候排除不符合要求的case。是分散的,低征用的,随机的。

package p123;

import java.util.concurrent.atomic.AtomicReference;

public class FeedOthers
   
   
    
     {
  AtomicReference
    
    
     
      head;
  AtomicReference
     
     
      
       tail;  

  public FeedOthers() {
    Node sentinel = new Node(null, NodeType.ITEM);
    head = new AtomicReference
      
      
       
       (sentinel);
    tail = new AtomicReference
       
       
         (sentinel); } public void enq(T e) { Node offer = new Node(e, NodeType.ITEM); while (true) { Node t = tail.get(); Node h = head.get(); if (h == t || t.type == NodeType.ITEM) { Node n = t.next.get(); if (t == tail.get()) { if (n != null) { tail.compareAndSet(t, n); } else if (t.next.compareAndSet(n, offer)) { tail.compareAndSet(t, offer); while (offer.item.get() == e); // spin h = head.get(); if (offer == h.next.get()) { head.compareAndSet(h, offer); } return; } } } else { Node n = h.next.get(); if (t != tail.get() || h != head.get() || n == null) { continue; // inconsistent snapshot } //fulltopic else if(n.type != NodeType.RESERVATION) { continue; } T other = n.item.get(); if(other.equals(e)) { // System.out.println("Can not eat from self "); continue; } boolean success = n.item.compareAndSet(other, e); head.compareAndSet(h, n); if (success) { return; } } } } public T deq(T me) { Node offer = new Node(me, NodeType.RESERVATION); //fulltopic while (true) { Node t = tail.get(); Node h = head.get(); if (h == t || t.type == NodeType.RESERVATION) { Node n = t.next.get(); if (t == tail.get()) { if (n != null) { tail.compareAndSet(t, n); } else if (t.next.compareAndSet(n, offer)) { tail.compareAndSet(t, offer); while (offer.item.get() == me); // spin h = head.get(); if (offer == h.next.get()) { head.compareAndSet(h, offer); } return offer.item.get(); } } } else { Node n = h.next.get(); if (t != tail.get() || h != head.get() || n == null) { continue; // inconsistent snapshot } //fulltopic else if(n.type != NodeType.ITEM) { continue; } T item = n.item.get(); if(item == null || item.equals(me)) { // System.out.println("Can not feed self "); continue; } boolean success = n.item.compareAndSet(item, null); head.compareAndSet(h, n); if (success) { return item; } } } } private enum NodeType {ITEM, RESERVATION}; private class Node { volatile NodeType type; volatile AtomicReference 
        
          item; volatile AtomicReference 
         
           next; Node(T item, NodeType type) { this.item = new AtomicReference 
          
            (item); this.next = new AtomicReference 
           
             (null); this.type = type; } public String toString() { return "Node[" + type + ", item: " + item + ", next: " + next + "]"; } } } 
            
           
          
         
       
      
      
     
     
    
    
   
   

124.

1. 应该以38行成功为线性化点,否则,

假设queue的状况是  head(A)-->B-->C-->sentinel

2个线程deq的执行顺序是  ThreadA.CAS --> ThreadB.CAS --> ThreadB.return --> ThreadA.return

以return为线性化点,返回结果应该是ThreadB得到A,ThreadA得到B; 但是算法的结果是 ThreadA得到A,ThreadB得到B。

2. enq的线性化点位16行成功。如果以tail更新为可线性化点,假设一个线程16行执行部成功,但是21行更新tail成功,则这个方法在没有实际enq的情况下使是方法的执行状态可见,这没有意义。

125.

1. enq是无等待的,因为每个调用能够在有限步完成。deq不是无锁的,假设只有一个deq线程,没有enq,这个线程运行无限步也不能完成一个调用。

2. 大概是这样:

deq()的可线性化点在13行成功地时刻。

对于enq和deq的操作,enq的可线性化点在第7行执行结束。

对于enq和enq的操作,enq的可线性化点在它们enq的值被deq成功的时刻,即第13行操作成功的时刻。

Reference: Reading the Linearizability paper of Herlihy and Wing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值