使 Java 的 ByteBuffer 线程安全的选项

问题描述 投票:0回答:2

我必须采取哪些选项才能使 ByteBuffer 线程安全?众所周知,它不是线程安全的,因为它的安全位置、限制和某些(/所有?)方法取决于此内部状态。

对于我的目的来说,如果多个读取线程是安全的就足够了,但对于其他未来的访问者,我想知道我需要知道哪些技术/技巧/陷阱才能使其完全线程安全。

我的想法:

  • 对所有方法同步或使用读写锁。可能是最慢的方法(?)
  • 子类化 ByteBuffer 并避免持久化线程绑定状态(如位置等)。并为所有需要使用内部状态的方法相应地抛出异常。这就是斋戒。但有没有陷阱呢? (除了我必须将直接映射的内存读取到堆内存中......)

我还可以使用哪些其他技巧?我会如何例如使用 DirectBuffer 实现“读取时克隆字节”——有可能吗?一种解决方案可能会涉及对整个 ByteBuffer (ByteBuffer.slice) 进行切片吗?

更新:这个问题“复制(同步时)以获取指向相同映射字节的新实例”的含义是什么

java thread-safety bytebuffer
2个回答
13
投票

Buffer 类可以成为线程安全的……从某种意义上说,各个操作都被正确同步,等等。然而,API 的设计并没有考虑到多线程,所以这可能是浪费时间。

基本问题是 Buffer 上的各个操作粒度太细,无法作为同步单元。应用程序无法在获取和放置操作或翻转、位置等级别进行有意义的同步。一般来说,应用程序需要原子地执行这些操作序列才能有效同步。

第二个问题是,如果您确实进行了精细的同步,这可能会增加方法调用的显着开销。由于使用 Buffer API 的目的是有效地执行 I/O,这违背了目的。


如果确实需要同步线程对共享缓冲区的访问,最好使用外部同步;例如像这样的:

    synchronized (someLock) {
        buffer.getByte();
        buffer.getLong();
        ...
    }

如果使用给定缓冲区的所有线程正确同步(例如使用相同的锁对象),则缓冲区不是线程安全的并不重要。线程安全是在缓冲区对象外部以更粗粒度的方式进行管理的。


正如评论所指出的,您还可以使用

ByteBuffer.slice()
buffer.asReadOnlyBuffer()
为您提供另一个缓冲区,并以现有缓冲区作为支持。然而,javadoc 并不保证这两种情况下的线程安全。事实上,Buffer
javadocs
做出了这样的笼统声明:

缓冲区对于多个并发线程使用并不安全。如果缓冲区要由多个线程使用,则应通过适当的同步来控制对该缓冲区的访问。


1
投票

使用 JDK13,您现在可以使用 ByteBuffer 而无需 byteBuffer.position(int),并且您可以获得“读取”线程安全性。

请参阅发行说明

java.nio.ByteBuffer 和 java.nio 中的其他缓冲区类型现在定义绝对批量 get 和 put 方法来传输连续的字节序列,而不考虑或影响缓冲区位置。

为了获得更好的解决方案,您应该使用新的外部内存 API。

© www.soinside.com 2019 - 2024. All rights reserved.