实践中的并发循环缓冲区错误?
Posted
技术标签:
【中文标题】实践中的并发循环缓冲区错误?【英文标题】:Concurrency In Practice Circular Buffer Error? 【发布时间】:2020-04-21 01:25:52 【问题描述】:最后阅读了优秀的Concurrency In Practice一书,我发现了BaseBoundedBuffer的清单14.2。照原样, put 和 take 方法将允许 count 超过缓冲区容量或低于 0。我知道该类是抽象的,但似乎很奇怪,这是默认行为。是否有一些充分的理由可以解释不允许计数超出容量或低于 0 的逻辑?也许像,
if(count != buf.length)
++count;
@ThreadSafe
public abstract class BaseBoundedBuffer<V>
@GuardedBy("this") private final V[] buf;
@GuardedBy("this") private final int tail;
@GuardedBy("this") private final int head;
@GuardedBy("this") private final int count;
protected BaseBoundedBuffer (int capacity)
this.buf = (V[]) new Object[capacity];
protected synchronized final void doPut(V v)
buf[tail] = v;
if (++tail == buf.length)
tail = 0;
++count;
protected synchronized final V doTake()
V v = buf[head];
buf[head] = null;
if (++head == buf.length)
head = 0;
--count;
return v;
public synchronized final boolean isFull()
return count == buf.length;
public synchronized final boolean isEmpty()
return count == 0;
【问题讨论】:
【参考方案1】:似乎给出了书中的示例子类,它的目的是让子类有责任在放入之前检查isFull
,在接受之前检查isEmpty
。有了这样的实现,再次检查是浪费时间。
@ThreadSafe
public class GrumpyBoundedBuffer<V> extends BaseBoundedBuffer<V>
public GrumpyBoundedBuffer(int size) super(size);
public synchronized void put(V v) throws BufferFullException
if (isFull())
throw new BufferFullException();
doPut(v);
public synchronized V take() throws BufferEmptyException
if (isEmpty())
throw new BufferEmptyException();
return doTake();
在现实世界中,一个适当的 JavaDoc 解释如何使用这些方法对于避免您发现的两个潜在错误至关重要。
不言而喻,书中的内容并不意味着它是正确的、最佳的,甚至是好的。您对实施持怀疑态度是对的。
【讨论】:
完全理解这不是复制和粘贴代码,但更令人惊讶的是,没有某种文档表明存在潜在的错误,并且实施者有一个很大的假设,特别是考虑到过去关于文档需求的章节。【参考方案2】:我们不应该让count
越界,但是这个例子假设检查这个条件被传播给调用者。我们不能只抛出异常,因为在多线程程序中,可能会以非异常方式预期和处理此类行为(例如,仅等待条件满足)。我们也不能只说if(count != buf.length) ++count;
,因为这将是处理逻辑的一部分,并且可能与调用者或子类中实现的逻辑发生冲突。
这个例子是更大图景的一部分——14.1.1. Example: propagating precondition failure to callers
章节描述了一种由子类处理异常情况的方法。本章描述了实现此类功能的两种“痛苦”方法(抛出异常或sleep
ing 线程),然后提供了一种更健壮的方法 - 使用条件队列(参见第 14.1.3 章)。
我想强调的是,您提到的代码示例不是复制粘贴的实现,它只是直截了当的方法。
【讨论】:
当然,我理解这不是要使用的代码,并且考虑到作者试图强调其他内容的章节的重点,但从教学的角度来看,我更惊讶的是会有一个大型的、未记录的假设,即必须以某种方式实现该类才能使其正常工作。 我认为基类本身就是为了不在不同的示例中重复代码。它不应该被复制或扩展到本章示例代码之外的任何地方——所以本章文本中的解释对我来说似乎是足够的文档。以上是关于实践中的并发循环缓冲区错误?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用连接缓冲区(块嵌套循环)错误修复MySql的LEFT JOIN?