简体   繁体   中英

Synchronized Methods in Java IO Streams

In Java since Java 1.0 in class java.io.InputStream there are methods

public synchronized void mark(int readlimit) {}

and

public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
}

Why are those two methods synchronized while all the others are not?

There are a few contradictory facts pointing that synchronized keyword is just a mistake here:

  1. For sure it is just a hint for developers. Methods are empty and synchronized keyword is not inherited in subclasses.

  2. On the other hand, other methods are not synchronized, even abstract and empty methods. That means we were warned not to forget about synchronization on mark/reset, but we were not warned about concurrent read() calls. That does not make sense because concurrent read will not work without synchronization.

  3. Many of the JDK stream implementations have incoherent usage of synchronized keywords.

  4. java.io.InputStream being put opposite to java.nio.Buffer has almost no useful basic method implementations but was made a class. So it tries to balance between this 'skeleton providing' and declaring general method contracts.

This is because of the reason, that mark() and reset() work together as you can see in the documentation.

public void mark(int readlimit): Marks the current position in this input stream. A subsequent call to the reset method repositions this stream at the last marked position so that subsequent reads re-read the same bytes.

If you have multiple threads which share the same InputStream, it could lead to problems, if these two methods wouldn't be synchronized.

Update to comment

java.io.InputStream is an abstract class so I think that the synchronized is more for the classes that inherits InputStream as a hint. The methods mark() and reset() will only be used, if markSupported() returns true. And in the class java.io.InputStream#markSupported() returns false.

/**
 * Tests if this input stream supports the <code>mark</code> and
 * <code>reset</code> methods. Whether or not <code>mark</code> and
 * <code>reset</code> are supported is an invariant property of a
 * particular input stream instance. The <code>markSupported</code> method
 * of <code>InputStream</code> returns <code>false</code>.
 *
 * @return  <code>true</code> if this stream instance supports the mark
 *          and reset methods; <code>false</code> otherwise.
 * @see     java.io.InputStream#mark(int)
 * @see     java.io.InputStream#reset()
 */
public boolean markSupported() {
    return false;
}

Since mark() and reset() methods have no code inside them, the word "synchronized" is only a "reminder" to implementing classes that they should put locks on or in these methods when they override them. This is to prevent race conditions on multi-threaded use-cases.

Now, other InputStream methods aren't marked as "synchronized" because these methods will never throw an IndexOutOfBoundsException, BufferOverflowException,etc. (unless you pass in bad buffer sizes). These methods always return a -1 when there are no more bytes to read rather than throw an Exception. So they don't need to be synchronized.

You'll notice that read() is abstract. And implementing classes do specify "synchronized" when they implement this method.

In other words, abstract InputStream class can handle multi-threads and implementing classes should also.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM