这个BoundedBuffer类的问题在哪里?

时间:2011-09-10 23:17:17

标签: java concurrency

我正在尝试构建的有界缓冲类的属性...

  • 多个生产者,多个消费者。
  • 阻止生产者并阻止消费者。
  • 使用AtomicInteger作为读/写指针。
  • 使用AtomicReferenceArray(采用泛型类型)来保存缓冲区。
  • 缓冲区的大小为Short.MAX_VALUE,它使用CAS来处理溢出。

现在问题......

问题:我似乎无法在下面的代码中注释掉 synchronized(this)blocks 。我认为使用AtomicInteger作为指针的重点是避免这样做。

注释掉synchronized(this)会阻止消费者丢失一些生产者放入的项目。如果我包含synchronized(this)块,那么一切都很好,并且生成的每一件东西都被消耗掉了。

我错过了什么?

public class BoundedBuffer<T> {
    private static final int BUFFER_SIZE = Short.MAX_VALUE+1;
    private AtomicReferenceArray<T> m_buffer = null;
    private Semaphore m_full = new Semaphore(BUFFER_SIZE);
    private Semaphore m_empty = new Semaphore(0);
    private AtomicInteger m_writePointer = new AtomicInteger();
    private AtomicInteger m_readPointer = new AtomicInteger();

    public BoundedBuffer() {
        m_buffer = new AtomicReferenceArray<T>(BUFFER_SIZE);
    }

    public static int safeGetAndIncrement(AtomicInteger i) {
        int oldValue = 0, newValue = 0;
        do {
            oldValue = i.get();
            newValue = (oldValue == Short.MAX_VALUE) ? 0 : (oldValue + 1);
        } while (!i.compareAndSet(oldValue, newValue));
        return oldValue;
    }

    public void add(T data) throws InterruptedException {
        m_full.acquire();
        synchronized (this) { // << Commenting this doesn't work
            // CAS-based overflow handling
            m_buffer.set(safeGetAndIncrement(m_writePointer),data);
        }
        m_empty.release();
    }

    public T get() throws InterruptedException {
        T data = null;
        m_empty.acquire();
        synchronized (this) { // << Commenting this doesn't work
            // CAS-based overflow handling
            data = m_buffer.get(safeGetAndIncrement(m_readPointer));
        }
        m_full.release();
        return data;
    }
}

1 个答案:

答案 0 :(得分:1)

可能存在这样的问题:当删除同步块时,数组中的get()不是原子的增量。我推测的破坏场景要求生产者超越消费者,然后你可以让生产者覆盖尚未读取的数组条目,如果信号量释放是由无序读取触发的。

考虑缓冲区已满(写入器索引为N,读取器索引为N + 1)并且2个线程正在尝试从缓冲区读取的情况。 (为简单起见,假设N不接近环绕点。)

线程1接收索引N + 1,从中读取其项目。

线程2接收索引N + 2,从中读取其项目。

由于调度的棘手,线程2首先从缓冲区数组获取并在线程1从数组中获取其项之前释放m_full信号量。

线程3(生产者)唤醒并将一个项目写入缓冲区中的下一个可用插槽N + 1,同样在线程1从缓冲区读取之前。

线程1然后获取索引N + 1的项目,但错过了它想要的项目。