9

My use case requires a directly allocated ByteBuffer that is written to once and thereafter read by many concurrent threads. All reads are absolute and so I'm never concerned with the buffer's state (position, limit, mark).

This article on byte buffers by Keith Gregory warns that even absolute reads are not considered thread-safe:

ByteBuffer thread safety is covered in the Buffer JavaDoc; the short version is that buffers are not thread-safe. Clearly, you can't use relative positioning from multiple threads without a race condition, but even absolute positioning is not guaranteed (regardless of what you might think after looking at the implementation classes).

(emphasis mine)

Because of this warning, I'm preceding every read from the byte buffer with a call to duplicate. This is easy enough, but the extra object allocation on every single read has me curious why it's actually necessary.

Despite Keith's wizardly disclaimer, I did look at OpenJDK's implementation of an absolute read from a direct byte buffer:

public byte get(int i) {
    return ((unsafe.getByte(ix(checkIndex(i)))));
}

You can see that it simply delegates to Unsafe.getByte(long), which "fetches a value from a given memory address".

I understand that different implementations could exist, but what reasonably couldn't be thread-safe about this operation? Does the Buffer contract simply decline to guarantee thread-safety for absolute reads in order to avoid the confusion of a partially thread-safe class? Or if the warning is justified for concurrent writes, what about my situation, in which the byte buffer is unmodified after creation? Also, would anything change when using a MappedByteBuffer instead?

Related:

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • Hmm. A lot of unsubstantiated opinion there. For example "I don't think this takes the Java memory model into account". The implementation *must* (a) obey the specification stated in the Javadoc and (b) take the Java memory model into account when doing so. – user207421 Nov 04 '14 at 06:30
  • @EJP The Java Memory Model remark seems to refer to direct buffers, which live outside the Java heap. They're basically memory-mapped IO operations, and IO operations on files (or anything outside the Java heap/stack/etc.) don't have to adhere to the Java Memory model – Erwin Bolwidt Nov 04 '14 at 08:18
  • 1
    Even when ignoring visibility issues you'll get without proper synchronization, the `get` method is not atomic. It accesses the shared data array and performs an index check which involves reading the shared state `limit`. – isnot2bad Nov 04 '14 at 09:21
  • 2
    *Nothing* is thread safe unless some mechanism has made it thread safe. – Raedwald Nov 04 '14 at 09:39
  • @PaulBellora - I added that line in response to a colleague who was sharing a buffer between threads. And my answer to you is the same as to him: yes, it certainly looks threadsafe to me, but I don't want to change a lot of code if we ever use a JVM where it isn't threadsafe. I believe the thread-local copy (shown lower in that article) is the best approach. I certainly *wouldn't* duplicate on every use. – kdgregory Nov 08 '14 at 12:45
  • @EJP - As I read [JLS 17.4.1](http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.1), the Java memory model applies *only* to on-heap memory accessed via Java's normal mechanisms (`getfield` *et al*). Any given buffer may or may not be backed by on-heap storage, and any given JVM may or may not take off-heap storage into account when erecting memory barriers. It's highly likely that X86 JVMs would use `MFENCE` for synchronized`, and my understanding of that operation is that it's a global barrier, but I'm not willing to bet on that implementation. – kdgregory Nov 08 '14 at 12:50
  • @kdgregory Thanks for your comments. Would you be able to post an answer addressing my questions? I'm particularly interested in why using `duplicate` isn't advisable, keeping in mind my buffer is effectively read-only after initialization. – Paul Bellora Nov 08 '14 at 19:02
  • @PaulBellora Going by the documentation of ByteBuffer only (i.e. not the implementation), invoking `duplicate` does not provide you with any safety guarantee unless you invoked `duplicate` with proper synchronization (e.g. inside a lock). The thread safety disclaimer in javadoc of Buffer did not provide any carve out for `duplicate`. – Haozhun Apr 26 '20 at 22:31

1 Answers1

-1

For one the Buffer documentation states that access to the buffer should be synchronized. It does no say that it must not be used by different threads. So I think a duplicate is not required.

That you can not think of a reasonable non-threadsafe implementation of some method or other is more a limit of your imagination than proof that caution is not necessary. Especially considering that you did not look at the Oracle Java code, when it was Oracle stating that the implementation is not threadsafe.

My advise: do some reasonable synchronization when accessing the buffer. Even if there never will be a non-threadsafe implementation it will not cost you much.

Thomas Stets
  • 3,015
  • 4
  • 17
  • 29
  • 2
    I preferred using `duplicate` over synchronization to avoid contention. Unfortunately your middle paragraph is more of a comment than an answer - my lack of proof and limited imagination are why I asked the question. – Paul Bellora Nov 04 '14 at 07:31
  • @Paul well there's the obvious case of an implementation reading/writing a byte at a time which would lead to torn reads/writes. – Voo Nov 04 '14 at 08:26
  • @Paul `duplicate` itself is not thread-safe, so you might not use it from different threads without proper synchronization too. – isnot2bad Nov 04 '14 at 09:22