Art of Multiprocessor Programming 答案 ch10

本文深入探讨了多种并发队列的实现方式,包括SynchronousDualQueue、TwoThreadLockFreeQueue等,并对其线程安全机制、无锁特性及线性化点进行了详细分析。

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

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、付费专栏及课程。

余额充值