3

我正在尝试实现 LZ77 的快速版本,我有一个关于并发编程的问题要问你。

现在我有 afinal byte[] buffer和 a final int[] resultHolder,两者的长度相同。该程序执行以下操作:

  1. 主线程写入所有缓冲区,然后通知线程并等待它们完成。

  2. 单个工作线程处理缓冲区的一部分,将结果保存在结果持有者的同一部分中。工人的部分是独家的。之后通知主线程并且工作人员暂停。

  3. 当所有工作人员都暂停后,主线程读取 resultHolder 中的数据并更新缓冲区,然后(如果需要)该过程再次从点 1 开始。

管理器(主线程)中的重要内容声明如下:

final byte[] buffer = new byte[SIZE];
final MemoryHelper memoryHelper = new MemoryHelper(); 
final ArrayBlockingQueue<Object> waitBuffer = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final ArrayBlockingQueue<Object> waitResult = new ArrayBlockingQueue<Object>(TOT_WORKERS);
final int[] resultHolder = new int[SIZE];

MemoryHelper 简单地包装了一个 volatile 字段并提供了两种方法:一种用于读取它,另一种用于写入它。

工人的 run() 代码:

public void run() {
    try {
        // Wait main thread
        while(manager.waitBuffer.take() != SHUTDOWN){
            // Load new buffer values
            manager.memoryHelper.readVolatile();

            // Do something
            for (int i = a; i <= b; i++){
                manager.resultHolder[i] = manager.buffer[i] + 10;
            }

            // Flush new values of resultHolder
            manager.memoryHelper.writeVolatile();
            // Signal job done
            manager.waitResult.add(Object.class);
        }
    } catch (InterruptedException e) { }
}

最后,主线程的重要部分:

for(int i=0; i < 100_000; i++){
    // Start workers
    for (int j = 0; j < TOT_WORKERS; j++)
        waitBuffer.add(Object.class);
    // Wait workers
    for (int j = 0; j < TOT_WORKERS; j++)
        waitResult.take();

    // Load results
    memoryHelper.readVolatile();
    // Do something
    processResult();
    setBuffer();
    // Store buffer
    memoryHelper.writeVolatile();
}

ArrayBlockingQueue 上的同步效果很好。我的疑问在于使用readVolatile()and writeVolatile()。有人告诉我,写入 volatile 字段会将所有先前更改的数据刷新到内存中,然后从另一个线程读取它会使它们可见。

那么在这种情况下是否足以确保正确的可见性?永远不会对相同的内存区域进行真正的并发访问,因此 volatile 字段应该比 ReadWriteLock 便宜很多。

4

1 回答 1

7

你甚至不需要volatile这里,因为BlockingQueues已经提供了必要的内存可见性保证:

内存一致性效果:与其他并发集合一样,线程中的操作在将对象放入BlockingQueue 之前的操作之前发生在BlockingQueue从另一个线程中访问或删除该元素之后的操作。

一般来说,如果你已经有某种同步,你可能不需要做任何特别的事情来确保内存可见性,因为你使用的同步原语已经保证了它。

但是,volatile当您没有显式同步时(例如在无锁算法中),可以使用读取和写入来确保内存可见性。

附言

此外,您似乎可以使用CyclicBarrier队列来代替您的解决方案,它专为类似场景而设计。

于 2014-04-22T13:37:57.797 回答