原子读取然后在Java中写入ByteBuffer的一部分

时间:2012-02-17 12:46:17

标签: java memory concurrency

我在java中有ByteBuffer,想要读取,然后有条件地修改该字节,例如使用如下方法:

public void updateByte(int index) {
    byte b = this.buffer.getByte(index);

    if (b == someByteValue) {
        this.buffer.setByte(index, someNewByte);
    }
}

如何确保读取然后修改字节是原子地发生的?

我不想同步整个ByteBuffer或updateByte方法,因为我希望多个线程能够同时读取/写入缓冲区的不同字节(即updateByte可以只要index不同,就可以被许多线程同时调用。

我使用的ByteBuffer没有字节[]支持,所以在上面的示例中为bb.hasArray() == false

7 个答案:

答案 0 :(得分:5)

如何为ByteBuffer的部分提供一组显式锁定对象(部分可能非常小,例如一个字,或者相当大,例如四个四分之一缓冲区)?

当一个线程要检查并修改一个字节时,它必须首先获取相应部分的锁,执行其工作,然后释放锁。

这将允许多个线程访问数据的不同部分,而不需要全局同步。

答案 1 :(得分:3)

我不相信你可以在Java中原子地访问字节。您可以做的最好是修改int值。这将允许您模拟修改单个字节。

您可以使用Unsafe(在许多JVM上)进行比较并交换数组()(堆ByteBuffer)或地址()(直接ByteBuffer)

答案 2 :(得分:3)

简短回答:如果不诉诸JNI,你就不能。

更长的答案:ByteBuffer API中没有原子更新。此外,没有严格定义ByteBuffer与内存的交互。在Sun实现中,用于访问原始内存的方法不会尝试刷新缓存,因此您可能会在多核处理器上看到过时的结果。

另外,请注意Buffer(及其子类,如ByteBuffer)被明确记录为不是线程安全的。如果有多个线程访问同一个缓冲区,则(1)依赖实现行为进行绝对访问,或(2)编写损坏的代码以进行相对访问。

答案 3 :(得分:1)

就个人而言,我会锁定一个互斥锁,直到找出将数据写入然后释放互斥锁的偏移量。这样你可以锁定很短的时间

答案 4 :(得分:1)

关于此list的并发DirectByteBuffer的长篇主题:

答案应为“是”。

另一个大例子是NIO.2。写/读操作提交字节缓冲区,调用CompletionHandler时可以。

因为在NIO.2情况下,只应用了DirectByteBuffer。对于“克隆”到DirectByteBuffer的非Direct-ByteBuffer,它不是真正的低级操作的参数。

答案 5 :(得分:0)

应该可以锁定ByteBuffer。方法:

  • 您可以创建一个锁定对象列表,并在每次读取bytebuffer时仅锁定一个区域。就像DNA所暗示的那样,这应该是最快的解决方案。
  • 或者你甚至可以使用memory mapping to solve this,然后使用FileChannel.lock,这也会锁定bytebuffer的一个区域但是更低级别。 修改:这仅保护来自外部程序的访问IMO
  • 或者您可以使用几个较小但同步的ByteBuffers +交换信息。它是interesting to note线程应该立即看到彼此之间的变化(这是我得到mmap想法的地方)

答案 6 :(得分:0)

我认为将代码的关键部分置于锁定控制之下应该是干净的解决方案。但是,如果您的用例与写入相比具有大量读取,则不要直接使用同步。我建议您使用ReentrantReadWriteLock作为解决方案的一部分。在修改ByteBuffer的函数中,您可以使用writeLock()。lock()然后使用代码。而阅读时则使用readLock()。lock()。您可以在上述链接上阅读有关读写锁定的更多信息。基本上它将允许并发读取而不是并发写入,而写入正在发生读取线程等待