Summary
Rewrite the writeXXX
methods defined by java.io.DataOutputStream
to its internal writeBuffer for temporary storage. This avoids excessive synchronization. Add text to the class description of both java.io.DataOutputStream
and java.io.DataInputStream
to make it clear that they are not safe for concurrent threads without appropriate synchronization.
Problem
Some Stream classes use very fine-grained locking.
In particular, writeInt is defined like this:
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
incCount(4);
Unfortunately, ByteArrayOutputStream.write(byte) is synchronized:
public synchronized void write(int b) {
...
}
leading to a monitor being acquired and released for every byte.
Solution
DataOutputStream already has an internal byte[] that it uses for temporary storage while outputting longs and doubles. We should use this buffer for ints, chars, and shorts too. Like so:
public final void writeInt(int v) throws IOException {
writeBuffer[0] = (byte)(v >>> 24);
writeBuffer[1] = (byte)(v >>> 16);
writeBuffer[2] = (byte)(v >>> 8);
writeBuffer[3] = (byte)(v >>> 0);
out.write(writeBuffer, 0, 4);
incCount(4);
}
Note that writeDouble() and writeLong() already do exactly this.
Specification
There is no specification issue as such: DataOutputStream is not specified to be thread safe. However, if an application writer assumed from the current implementation that racy access to a DataOutputStream was thread safe, it will no longer be. This is because each instance of DataOutputStream uses an internal buffer.
However, to make the situation clear I have added language to warn users of DataOutputStream that it is not thread safe. I have also modified the language used in DataInputStream to make it less ambiguous.
- csr of
-
JDK-8254078 DataOutputStream is very slow post-disabling of Biased Locking
- Resolved