Synchronized Methods in Java IO Streams
Asked Answered
U

3

27

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?

Utgardloki answered 4/11, 2015 at 15:39 Comment(8)
There is no practical reason for them to be synchronized, maybe it's just a hint for InputStream subclass authors. Furthermore, it's very hard to imagine a realistic scenario when sharing a stream between two or more threads would be useful at all. (In fact it's hard to imagine a scenario where it wouldn't lead to disaster.)Alexalexa
@biziclop, I'm of the same opinion. It looks like mistake or rudiment. I just want to hear other opinions. Thanks!Utgardloki
Uh oh, now I have looked into the source code… Unnecessary synchronization is not as bad as the fact that the base implementation of read(byte[],int,int) swallows IOExceptions…Ploce
@Ploce The implementation of skip() is quite peculiar too. And the fact that InputStream isn't an interface in the first place, given the uselessness of the skeleton implementations provided.Alexalexa
I’m also impressed by the code of BufferedInputStream. It eventually declares all read methods, skip, mark and reset as synchronized, ensuring indeed a consistent thread-safe behavior across all these operations. Then it provides an optimized close operation using CAS. I’m wondering what the authors think how often a stream gets closed, typically, compared to all the other operations named above…Ploce
If you give a test to OutputStreamWriter, you'll be impressed by performance as well. It works slow like a hell and sources are not available.Utgardloki
@Ploce The re-implementation of close() could be the result of some kind of mass "clean-up" process when the Closeable interface was introduced.Alexalexa
@biziclop: indeed, both, the Closeable interface and the AtomicReferenceFieldUpdater were introduced with Java 5. Still, that “cleanup” looks more like an unnecessary complication. Ironically, FileInputStream.close uses synchronizedPloce
I
6

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.

Incognizant answered 16/11, 2015 at 16:14 Comment(0)
G
0

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;
}
Gin answered 4/11, 2015 at 16:3 Comment(6)
What kind of problem could happen to an empty method body?Utgardloki
As for me, it looks like a correct answer to a different questionUtgardloki
@Antonio maybe I didn't get you right, my english isn't the best ;). Sorry for that, I hope an other person can help you.Gin
the synchronized keyword isn't inherited by subclasses, how can it do any good for the classes that inherit from InputStream?Potassium
Even if you suppose that the synchronized modifier is just a hint for subclasses pointing to the need of synchronization, it would be inconsistent as the intended purpose of mark()/reset() implies that these operations must not interfere with the read operations as well. But the read methods are not synchronizedPloce
Yeah you're right, silly me! I'm sorry for the wrong approach.Gin
G
-1

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.

Glutton answered 14/11, 2015 at 8:36 Comment(2)
The second paragraph is complete nonsense. Array index exceptions don't have anything do to with synchronization.Haemophiliac
Huh? In a multi-threaded environment when arrays are involved like it is in InputStream, you can get all sorts of exceptions if you don't put in the proper locks.Glutton

© 2022 - 2024 — McMap. All rights reserved.