August 17, 1998

Question

According to Sun , "The byte streams are synchronized on each stream object." (Ken Arnold and James Gosling, The Java Programming Language, 2nd Edition, p. 231) However, lately I've noticed that this doesn't actually seem to be the case for many stream classes, so today's question is: Just how thread safe are streams anyway?

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:

  1. Is there some thread safety constraint on stream methods I'm just not seeing?
  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"
  3. Should stream methods be declared synchronized?
  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'm also looking for citations of anywhere Sun has said that the Java class library is guaranteed to be thread safe or anything like that. I do remember this being said, but I can't for the life of me find it anywhere. (It could have been part of an early white paper or tutorial that's no longer available.) Certainly if the class library isn't thread safe, then it's beholden on Sun to document exactly what isn't. (Vectors and enumerations in java.util are another common instance of non-thread safe classes.)

Answer

This question didn't inspire many answers. I suspect most programmers aren't paying a lot of attention to thread safety issues. In fact I got exactly one answer, from Michael Hermann:

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 your

int 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).


[ Cafe au Lait | Books | Trade Shows | Links | FAQ | Tutorial | User Groups ]

Copyright 1998 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified August 17, 1998