所以我首先要说的是这是一项任务,对我需要构建的队列有一些限制:
这是无限的。
不允许使用锁定机制(没有synchronized
,ReentrantLock
等){甚至不能模拟像CompareAndSet这样的东西}。
必须实施Michael and Scott's算法。
当然,我无法剔除ConcurrentLinkedQueue
的Java源代码。
这是我到目前为止所做的事情(遗漏了):
public class LockFreeQueue<T> implements MyQueue<T>
{
private class Node<R>
{
public final R value;
public volatile AtomicReference<Node<R>> next;
public Node()
{
value = null;
next = new AtomicReference<Node<R>>(null);
}
public Node(R v)
{
value = v;
next = new AtomicReference<Node<R>>(null);
}
}
volatile AtomicStampedReference<Node<T>> head = new AtomicStampedReference<Node<T>>(new Node<T>(), 0);
volatile AtomicStampedReference<Node<T>> tail = head;
AtomicInteger count = new AtomicInteger(0);
public boolean enq(T value)
{
Node<T> node = new Node<T>(value);
while (true)
{
Node<T> tempTail = tail.getReference();
int tailCount = tail.getStamp();
Node<T> next = tempTail.next.get();
if (tempTail == tail.getReference())
{
if (null == next)
{
if(tempTail.next.compareAndSet(null, node))
{
// Problem on this line:
tail.compareAndSet(tempTail, node, tailCount, count.incrementAndGet());
return true;
}
}
else
{
tail.compareAndSet(tempTail, next, tailCount, count.incrementAndGet());
}
}
}
}
...
}
现在,在大多数情况下,这遵循this IBM article中的代码。特别是,它遵循Listing 4. Insertion in the Michael-Scott nonblocking queue algorithm
中的代码。我的代码和文章中的代码所展示的问题是,当尾部的引用转向新的尾部时,它对头部也是如此。这样做当然是因为它改变了被引用的Node
,而不是实际的引用,tail
和head
的引用是相同的。
我还发现了a different solution,但此代码的主要问题是它使用synchronized
,即使它只是模拟CAS操作。
就Java的ConcurrentLinkedQueue
而言,我注意到他们使用AtomicReferenceUpdater
,这似乎是唯一可行的方法。不过,这里的问题是我的代码开始看起来非常接近Java,而且我不想成为剽窃的停靠点(或更糟糕的!)。
AtomicReferenceUpdater
是我前进的唯一途径,还是我可以做的其他事情?
最后一点:
Node
是我自己的完全捏造,所以它可以变成任何它需要的东西。修改
Ben Manes提到不需要担心垃圾收集语言中的ABA问题。对于那些对此感兴趣的人,有this article这是一个很好的阅读。重要的部分:
在垃圾收集语言中,这不是问题。为什么?因为在观察包含指向结构的指针的线程释放它们之前,无法为新对象回收节点的内存。