Consider, for example, the readInt()
method in java.io.DataInputStream
public final int readInt() throws IOException {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
The individual calls to in.read()
are atomic,
but the complete readInt()
method is not.
What if a different thread interrupts this method after ch2
is read and
before ch3
is read? Suppose further that
the interrupting thread proceeds to read
some bytes from this very same input stream.
When this method begins executing again, the ch3
and ch4
that it then
reads will have no necessary realtionto the ch1
and ch2
already read.
Whatever data it reads must be considered corrupt.
This wouldn't be hard for Sun to fix, but it would have some
performance implications. The easiest fix is to simply
declare readInt()
synchronized:
public final synchronized int readInt() throws IOException {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
A slightly more robust alternative synchronizes the underlying input stream
instead:
public final int readInt() throws IOException {
synchronized(this.in) {
InputStream in = this.in;
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
}
Neither of these is implemented in any current versions of Java I've seen.
You could do something similar by wrapping a DataInputStream
object inside your own stream class that provided the same interface
but declared all methods synchronized.
This problem is most obvious in filter streams like DataInputStream
and
DataOutputStream
, but it applies to any stream that needs to insure that related
bytes are kept together.
So the question this week is really four-fold:
synchronized
?
System.out
, System.in
, and System.err
)
in two different threads?
java.util
are another common instance of
non-thread safe classes.)
I doubt that Sun can easily fix this as you require synchronisation across methods. At best you could add a method setInUse and block any thread but the one which called setInUse(true) from using the stream until setInUse(false) is called by the same thread. Synchronizing the stream-methods alone will not help you as you demonstrated with yourint ch1 = in.read(); int ch2 = in.read();
example.
Furthermore your second example IMO is a lot more secure than the first as it synchronizes on the stream, rather than the object reading from it. Either way the synchronization has to be taken care of by the programmer as more than a single stream-method has to be synchronized. As a rule of thumb, I synchronize on the stream object when reading or writing to it using more than just one call.
Concerning your questions :
1. Is there some thread safety constraint on stream methods I'm just not seeing?
Not that I am aware of
2. Is the behavior of streams in multi-threaded situtations documented anywhere?
Even as a rule of thumb like "Never use the same stream in two different threads"
My rule of thumb is : synchronize on the stream-object if you make several calls to the stream (read or write).
I don't know of any 'official' rules.
3. Should stream methods be declared synchronized?
Even if they are (perhaps some are), this wouldn't shield you from explicitly synchronizing the above example, so this alone will not suffice.
4. Is there ever a good reason to use the same stream (other than System.out, System.in, and System.err) in two different threads?
I can't come up with any right away, except for a logfile (but this is basically the same as System.err).