0

假设我已经映射了一个内存区域 [0, 1000],现在我有了 MappedByteBuffer。

假设每个线程为 exp 访问缓冲区的不同部分,我是否可以同时从多个线程读取和写入此缓冲区而无需锁定。T1 [0, 500), T2 [500, 1000]?

如果上述情况属实,是否可以确定是为多个线程创建一个大缓冲区,还是为每个线程创建一个较小的缓冲区?

4

1 回答 1

1

详细介绍:

如果您想学习如何自己回答这些问题,请查看它们的实现源代码:

  • MappedByteBuffer:https ://github.com/himnay/java7-sourcecode/blob/master/java/nio/MappedByteBuffer.java (注意它仍然是抽象的,所以你不能直接实例化它)
  • 扩展 ByteBuffer:https ://github.com/himnay/java7-sourcecode/blob/master/java/nio/ByteBuffer.java
  • 扩展缓冲区:https ://github.com/himnay/java7-sourcecode/blob/329bbb33cbe8620aee3cee533eec346b4b56facd/java/nio/Buffer.java (仅进行索引检查,不授予对任何缓冲存储器的实际访问权限)

现在它变得有点复杂:

当你想分配一个 MappedByteBuffer 时,你会得到一个

不必浏览互联网页面,您还可以简单地下载 Java 版本的源代码包并将它们附加到您的 IDE 中,这样您就可以在开发和调试模式下查看代码。轻松很多。

简短(不完整)的答案:

它们都不能防止多线程。

  • 因此,如果您需要调整 MappedByteBuffer 的大小,您可能会获得陈旧甚至糟糕的 (ArrayIndexOutOfBoundsException) 访问权限
  • 如果大小是恒定的,就您的要求而言,您可以依靠任一实现来实现“线程安全”

附带说明一下,Java 实现中还有一个实现失败的蔓延:

  • MappedByteBuffer 扩展了 ByteBuffer
  • ByteBuffer 有一个byte[]叫做“hb”的堆
  • DirectByteBuffer 扩展 MappedByteBuffer 扩展 ByteBuffer
  • 所以 DirectByteBuffer 还是有 ByteBuffer 的byte[] hb缓冲区,
    • 但不使用它
    • 而是创建和管理自己的 Buffer

这个设计缺陷来自于这些类的逐步开发(它们不是同时计划和实现的),以及包可见性的主题,导致实现的依赖/层次结构的反转。

现在到真正的答案:

如果你想做正确的面向对象编程,除非绝对需要,否则不应该共享资源。这特别意味着每个线程都应该有自己的缓冲区。

拥有一个全局缓冲区的优势:唯一的“优势”是减少额外对象引用的额外内存消耗。但是这种影响非常(甚至不会在您的应用程序 RAM 消耗中发生 1:10000 的变化),以至于您永远不会注意到它。由于各种奇怪的(Java)原因,到处都有许多其他对象被分配,这是您最不关心的问题。另外,您将不得不引入额外的数据(索引边界),这会进一步降低“优势”。

拥有单独缓冲区的最大优势:

  • 您将永远不必处理指针/索引算术
    • 特别是当您在任何给定时间需要更多线程时
  • 您可以随时自由分配新线程,而无需重新排列任何数据或进行更多指针运算
  • 您可以在需要时自由地重新分配/调整每个单独的缓冲区(无需担心所有其他线程的索引要求)
  • 调试:您可以更轻松地定位“越界写入”导致的问题,因为如果他们尝试,坏线程会崩溃,而不是其他必须处理损坏数据的线程
    • Java 总是在访问它之前检查每个数组访问(在普通堆数组上byte[]),以防止副作用
    • 回想一下:曾几何时,操作系统在引入线性地址空间方面迈出了一大步,因此程序不必关心它们在硬件 RAM 中的加载位置。
    • 您的单缓冲区设计将是确切的倒退。

结论:

如果你想要一个非常糟糕的设计选择——这会让以后的生活变得更加艰难——你可以选择一个全局 Buffer

如果您想以正确的 OO 方式进行操作,请将这些缓冲区分开。没有复杂的依赖和副作用问题。

于 2021-09-16T12:32:50.270 回答