12

我有哪些选项可以使 ByteBuffer 线程安全?众所周知,它不是线程安全的,因为它保护位置、限制和一些(/全部?)方法取决于这个内部状态。

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

我的想法是:

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

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

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

4

2 回答 2

13

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

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

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


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

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

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


正如评论指出的那样,您还可以使用ByteBuffer.slice()buffer.asReadOnlyBuffer()给您另一个缓冲区,现有缓冲区作为支持。但是,javadocs 在这两种情况下都不保证线程安全。事实上,javadocs做出Buffer了这样的概括性声明:

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

于 2012-06-22T10:35:03.053 回答
0

使用 JDK13,您现在可以在没有 byteBuffer.position(int) 的情况下使用 ByteBuffer 并获得线程安全。

请参阅发行说明

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

于 2020-06-17T11:12:19.040 回答