byte array read and write in multithreads
我为线程分配了一个非常大的字节数组,以读取和写入数组中的字节。写入操作是将新字节直接分配给数组中的值,例如byte[i] = byte2;
我可以确保这些操作不会发生冲突(字节数组中的同一部分将不会由2个不同的线程进行写入。读取部分时,其他线程将不会执行任何写入操作)。我担心的是对数组的修改是否可立即用于其他线程。我知道可能存在内存隔离,哪些其他线程可能仍会读取数组中的旧值。
如果存在问题,如何避免? volatile byte[] store;在这种情况下可以工作吗?
will the modification to array been instantly available to other threads.
并非没有写者的写保护区和读者的读保护区。
I know there might be memory fence, which other threads may still read the old values in the array.
您将需要为读取器和写入器使用其中之一(不是每个字节一个,每个操作一个)
will volatile byte[] store; works in this circumstance?
它不会执行您想要的操作,尽管它会减慢您的应用程序的速度。当你做
当您正在读取对字节[]的store引用时,这增加了一个读取篱笆。
最好的解决方案是使用Unsafe完全控制这些读写操作。请参阅AtomicIntegerArray的代码。
但是,如果不走这条路,您可以执行这样的虚拟操作,虽然效率不高,但实现起来却简单得多。
1 2 3 4 5 6 7 8 9
| private final AtomicBoolean fence = new AtomicBoolean();
public void readFence() {
fence.get();
}
public void writeFence() {
fence.set(true);
} |
要使其正常工作,必须在所有写入之后执行writeFence(),并在所有读取之前执行readFence()。
如果您走的是更底层的路由,则可能会发现使用堆外内存是一个优势。堆外内存可以存储大量(TB)的数据,而不会影响GC。
- 为什么volatile在他的情况下不起作用?它将确保所有线程始终具有适当的效用,不是吗? Nvm,我发现了这一点:stackoverflow.com/questions/2236184/…
-
@ChristopheDeTroyer正确,所有线程将使用读取围栅读取byte[]引用的正确值。没有写限制,并且byte []的内容可能不可见。与final类似的volatile仅适用于对数组的引用,不适用于数组上的操作。例如final bytes[] store = { .. },您可以更改内容。您可以做一个虚拟store = store;来触发写栅栏,但是我发现这比建议的要麻烦得多。另一个缺点是每个操作上都有一个内存屏障,可能会使副本速度降低一个数量级。
-
谢谢,我想我可以在AtomicIntegerArray中找到我的答案。我仍然不明白您的示例中的fence对商店进行写防护的用途。
-
@Shisoft在执行fence.set(true)时会使用写屏障,它将为之前发生的所有写入提供保证。当您进行任何线程安全阅读时,例如fence.get()这将确保此后的每次读取都将在该点看到最新数据。 (即使它在概念上与它无关)
-
另一个简短的问题是,AtomicIntegerArray是否已退出堆栈?看起来很可能是因为堆外也使用不安全的方法来完成这项工作。
-
@Shisoft,它不是没有堆,但可以修改。如果您想降低层次,请查看Chronicle-Bytes github.com/OpenHFT/Chronicle-Bytes,它支持堆数组和线程安全的任意大小的堆块。注意:堆外块可以持久保存到文件中,并且可以在同一台机器上的JVM之间共享。
-
我知道您知道,但是我认为提及旧的"为自己分配易失性数组"技巧仍然很有趣。如果您不想使用AtomicArray类,那仍然是唯一的可移植方式(理论上讲,不安全是一种实现细节)。
-
我现在正在审查不安全的方法,putXXX和getXXX方法是避免我的情况下发生线程缓存或内存隔离的解决方案吗?
-
@Shisoft它们使您可以在没有围栏的情况下访问内存,并且您可以通过xxxVolatileXxx和putOrderedXxx方法有选择地使用围栏。
-
我已经迁移了我的应用程序以对后端存储使用不安全。它比我的字节数组解决方案似乎更易于使用,因为它具有用于主要类型的内部编码器和解码器,并且能够突破2G大小的限制。
-
如果您使用github.com/OpenHFT/Chronicle-Bytes支持边界检查,那么您就不会破坏内存,但具有线程安全的操作,并且大小也不受限制。它支持内存映射文件,这意味着您的数据可在重新启动后立即可用。它还支持文本和复杂的二进制编码。例如。 MappedBytes是一个无限制的字节,它会根据需要(以及基础文件)增长
您可以为此使用SynchronizedList
例子
它将是线程安全的
-
您必须每次都替换整个byte[]才能起作用。对于byte []的某些部分将不起作用。
-
其实我不想在字节数组上做任何复制