Options to make Java's ByteBuffer thread safe

Karussell picture Karussell · Jun 22, 2012 · Viewed 9.6k times · Source

What options do I have to make a ByteBuffer thread safe? It is known that it is not thread safe as it safes position, limit and some(/all?) methods depend on this internal state.

For my purposes it will be sufficient if multiple read-threads are safe, but for other future visitors I would like to know what technics/tricks/traps I need to know to make it completely thread safe.

What I have in mind:

  • Synchronizing or using ReadWrite locks for all methods. Probably the slowest approach (?)
  • Subclassing ByteBuffer and avoid persisting thread-bound state like position etc. And throwing exceptions accordingly for all methods which need to use internal state. This would be the fastes. But are there any traps? (except that I'll have to read the directly mapped memory into heap memory ...)

What other tricks could I use? How would I e.g. implement a "clone bytes on read" with DirectBuffer - is it possible at all? Would probably slicing the full ByteBuffer (ByteBuffer.slice) be involved in one solution?

Update: What is meant in this question with "duplicate (while synchronized) to get a new instance pointing to the same mapped bytes"

Answer

Stephen C picture Stephen C · Jun 22, 2012

A Buffer class could be made thread-safe ... in the sense that the individual operations were properly synchronized, etcetera. However, the API is not designed with multiple threads in mind, so this this is probably a waste of time.

The basic problem is that the individual operations on a Buffer are too fine-grained to be the unit of synchronization. An application cannot meaningfully synchronize at the level of the get and put operations, or flip, position and so on. Generally speaking, an application needs to perform sequences of these operations atomically in order to synchronize effectively.

The second problem is that if you do synchronize at a fine level, this is likely to add significant overheads on the method calls. Since the point of using the Buffer APIs is to do I/O efficiently, this defeats the purpose.


If you do need to synchronize thread access to a shared buffer, it is better to use external synchronization; e.g. something like this:

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

Provided all threads that use a given buffer synchronize properly (e.g. using the same lock object), it doesn't matter that the Buffer is not thread-safe. Thread safety is managed externally to the buffer object, and in a more coarse-grained fashion.


As comments point out, you could also use ByteBuffer.slice() or buffer.asReadOnlyBuffer() to give you another buffer with the existing one as backing. However, the javadocs do not guarantee thread-safety in either case. Indeed, the javadocs for Buffer make this blanket statement:

Buffers are not safe for use by multiple concurrent threads. If a buffer is to be used by more than one thread then access to the buffer should be controlled by appropriate synchronization.