首页 > 解决方案 > java.nio.ByteBuffer.slice() 线程行为?

问题描述

我知道 java.nio.ByteBuffer 本身不是线程安全的。但是,如果您通过 slice() 获得一个共享的、派生的 ByteBuffer,您是否能够通过不同的切片缓冲区在多个线程中同时访问底层缓冲区的内容?我在 API 规范中找不到任何关于此的内容...如果此行为未标准化,您知道它是如何在最常见的 VM 中实现的吗?

标签: javamultithreadingbytebuffer

解决方案


基本上,如果某些东西没有被证明是线程安全的,那么就假设它不是;如果某些东西被明确记录为不是线程安全的,除非另有说明,否则永远不要假设任何密切相关的东西都是线程安全的。


正如您所提到的,缓冲区不是线程安全的。这由以下人员记录Buffer

多个并发线程使用缓冲区是不安全的。如果一个缓冲区要被多个线程使用,那么对缓冲区的访问应该由适当的同步控制。

ByteBuffer并且扩展的文档与Buffer上述内容不矛盾。

这是文档中的内容ByteBuffer#slice()

创建一个新的字节缓冲区,其内容是此缓冲区内容的共享子序列[强调添加]。

新缓冲区的内容将从该缓冲区的当前位置开始。此缓冲区内容的更改将在新缓冲区中可见,反之亦然;两个缓冲区的位置、限制和标记值将是独立的。[重点补充]

新缓冲区的位置将为零,其容量和限制将是此缓冲区中剩余的字节数,其标记将是未定义的,其字节顺序将为BIG_ENDIAN。当且仅当此缓冲区是直接的时,新缓冲区将是直接的,并且当且仅当此缓冲区是只读的时,它将是只读的。

其他类似的方法,例如#slice(int,int)and #alignedSlice(int),记录了类似的行为。

如您所见,缓冲区实例的内容是共享的。文档没有提到在这种情况下添加线程安全的任何内容,因此我们可以自信地假设缓冲区的一般线程安全适用——也就是说,没有线程安全。如果写入共享相同内容子序列的任何缓冲区,则所有其他缓冲区都将受到影响。在并发上下文中,如果没有适当的外部同步,这意味着潜在的竞争条件。

我不肯定这如何适用于读取和写入不同(即非重叠)子序列。我假设适用于数组的任何行为都适用于这种情况。当然,这并没有考虑到直接缓冲区。

综上所述,这其中有一些微妙之处。如文档所述,每个缓冲区都有独立的位置、限制和标记值。这样做的结果是每个缓冲区都可以由单独的线程读取。但是,这是缓冲区和线程之间的一对一映射(除非您添加外部同步)1。这是因为仅通过读取缓冲区(至少,相对读取操作1就是这种情况)和倒带就可以修改位置和标记值。


1.我相信当且仅当它们都使用绝对读取操作并且不使用标记时,多个线程可以在不同步的情况下从同一个缓冲区实例中读取。换句话说,只要没有线程修改缓冲区的“元状态”。


推荐阅读