我们有一个专家,多生产者(用户)和单一消费者(Engine)队列。用户线程运行频率更高,并始终将单个元素添加到队列中。 Engine线程操作运行频率较低,并批量处理堆栈元素。如果堆栈为空,它将停放,直到用户线程添加了一个条目。这样,只有当队列从空变为1时才需要进行通知。
在这个实现中,不是Engine线程迭代并一次删除一个项目,而是将它们全部删除 - drainAll,而不是drainTo。没有其他操作可以改变堆栈 - 只是用户线程添加,以及引擎线程drainAll。
目前我们通过同步链表执行此操作,我们想知道是否有非阻塞方式来执行此操作。对JDK类的drainTo操作将迭代堆栈,我们只想在一次操作中获取堆栈中的所有内容,而不进行迭代 - 因为每次迭代都会遇到volatile / cas相关逻辑,所以我们理想情况下只需要点击一次,每个排水管。引擎线程可以迭代并操作每个单独的元素,而无需触及sync / volatile / cas操作。
当前的实现类似于:
public class SynchronizedPropagationQueue implements PropagatioQueue {
protected volatile PropagationEntry head;
protected volatile PropagationEntry tail;
protected synchronized void addEntry( PropagationEntry entry ) {
if ( head == null ) {
head = entry;
notifyWaitOnRest();
} else {
tail.setNext( entry );
}
tail = entry;
}
@Override
public synchronized PropagationEntry drainAll() {
PropagationEntry currentHead = head;
head = null;
tail = null;
return currentHead;
}
public synchronized void waitOnRest() {
try {
log.debug("Engine wait");
wait();
} catch (InterruptedException e) {
// do nothing
}
log.debug("Engine resumed");
}
@Override
public synchronized void notifyWaitOnRest() {
notifyAll();
}
}
ASDF
答案 0 :(得分:0)
Stacks有一个非常简单的非阻塞实现,支持并发" pop all"操作容易,并且可以容易地检测到空的>非空转换。您可以让所有生产者将项目推送到堆栈,然后让引擎立即清空整个项目。它看起来像这样:
public class EngineQueue<T>
{
private final AtomicReference<Node<T>> m_lastItem = new AtomicReference<>();
public void add(T item)
{
Node<T> newNode = new Node<T>(item);
do {
newNode.m_next = m_lastItem.get();
} while(!m_lastItem.compareAndSet(newNode.m_next, newNode));
if (newNode.m_next == null)
{
// ... just went non-empty signal any waiting consumer
}
}
public List<T> removeAll()
{
Node<T> stack = m_lastItem.getAndSet(null);
// ... wait for non-empty if necessary
List<T> ret = new ArrayList<>();
for (;stack != null; stack=stack.m_next)
{
ret.add(stack.m_data);
}
Collections.reverse(ret);
return ret;
}
private static class Node<U>
{
Node<U> m_next;
final U m_data;
Node(U data)
{
super();
m_data = data;
}
}
}
对于空的信号 - &gt;非空转换,可以使用正常同步。如果你只是在检测到空状态时这样做,那就不会很昂贵......因为你只有在你失去工作时才能进入空状态。
答案 1 :(得分:0)
目前我们通过同步链表执行此操作,我们想知道是否有非阻塞方式来执行此操作。 JDK类上的drainTo操作将迭代堆栈,我们只想在一次操作中获取堆栈中的所有内容,而不进行迭代
也许我不明白,但似乎使用BlockingQueue.drainTo(...)
方法会比您的实施更好。例如,LinkedBlockingQueue.drainTo(...)
方法只围绕该方法锁定一次 - 我看到没有迭代开销。
如果这不是学术讨论,那么我怀疑你的性能问题与队列本身有关,并将你的工作集中在其他方面。如果它是学术性的,那么@Matt的答案可能会更好,尽管肯定会有更多代码要编写以支持完整的Collection
方法列表。