在java中编写线程安全的阻塞有界缓冲区结构时,如何处理索引溢出?

时间:2011-09-10 19:21:38

标签: java concurrency

更新:我已根据下面的正确答案更新了代码。这有效,但会产生一个新问题(我将发布一个新问题)。

使用具有多个生产者和消费者的信号量创建一个阻塞有界缓冲类。

目标是使用原子整数作为指针,因此我不必在内部进行同步。溢流处理已更正为现在使用CAS。

但是这不起作用,除非我在AtomicInteger指针周围使用synchronized(参见下面的评论)。 不确定原因?滚动到下方,看看“缺少参赛作品”是什么意思......

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;
}
}

测试程序有......

8个生产者线程,每个都将大约10000个条目放入队列。   每个条目都是一个格式的字符串:A“:”B   哪里   A是8个线程的数字0..7。   B只是从0..9999起数字增加的数量

4个消费者线程,消耗所有内容,直到命中null。

一旦生产者线程完成了将所有内容添加到缓冲区中,就会将4个null添加到队列中(以阻止使用者)。

线程输出......

P:数据,1:9654 @ 1 P:数据,5:1097 @ 347 C:数据,1:9654 @ 1 P:数据,4:5538 @ 1 C:数据,4:5538 @ 1 C:数据,null @ 14466

验证 在验证所有生成的条目是否都是消费者时,会有一些条目丢失(就在敲击arrayindexoutofbounds之前(可能只是巧合)。

...验证 失踪4:5537 失踪5:1096 验证

1 个答案:

答案 0 :(得分:2)

递增计数器时需要处理溢出。例如,您可以使用以下方法代替getAndIncrement()

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

它使用典型的compare-and-swap方法,不应该损害性能,因为getAndIncrement()在内部以相同的方式实现。

此外,如果MAX_VALUEBUFFER_SIZE,则您不需要% BUFFER_SIZE操作。