Синхронизированные методы в потоках ввода-вывода Java
В Java с Java 1.0 в классе java.io.InputStream
есть методы
public synchronized void mark(int readlimit) {}
и
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
Почему эти два метода синхронизируются, а все остальные не являются?
Ответы
Ответ 1
Есть несколько противоречивых фактов, указывающих на то, что синхронизированное ключевое слово здесь просто ошибка:
-
Конечно, это всего лишь подсказка для разработчиков. Методы пусты, а ключевое слово synchronized
не наследуется в подклассах.
-
С другой стороны, другие методы не синхронизированы, даже абстрактные и пустые методы. Это означает, что нас предупреждали не забывать о синхронизации на mark/ reset, но мы не были предупреждены о параллельных вызовах read()
. Это не имеет смысла, потому что одновременное чтение не будет работать без синхронизации.
-
Многие из реализаций потока JDK имеют некогерентное использование синхронизированных ключевых слов.
-
java.io.InputStream
, поставленный напротив java.nio.Buffer
, почти не имеет полезных базовых методов реализации, но был сделан классом. Поэтому он пытается балансировать между этим "скелетным обеспечением" и объявлением контрактов общего метода.
Ответ 2
Это связано с тем, что метки() и reset() работают вместе, как вы можете видеть в документации.
public void mark (int readlimit):Помечает текущую позицию в этом входном потоке. Последующий вызов метода reset
репозиционирует этот поток с последним отмеченным чтобы последующие чтения перечитывали одни и те же байты.
Если у вас несколько потоков, которые используют один и тот же InputStream, это может привести к проблемам, если эти два метода не будут синхронизированы.
Обновить комментарий
java.io.InputStream
является абстрактным классом, поэтому я считаю, что синхронизировано больше для классов, которые наследуют InputStream как подсказку. Методы mark()
и reset()
будут использоваться, только если markSupported()
возвращает true. И в классе java.io.InputStream#markSupported()
возвращается 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;
}
Ответ 3
Так как методы mark() и reset() не содержат внутри них кода, слово "synchronized" является всего лишь "напоминанием" для реализации классов, в которые они должны помещать блокировки или в эти методы, когда они переопределяют их. Это делается для предотвращения условий гонки в многопоточных вариантах использования.
Теперь другие методы InputStream не помечены как "синхронизированные", потому что эти методы никогда не будут бросать IndexOutOfBoundsException, BufferOverflowException и т.д. (за исключением случаев, когда вы проходите в плохих размерах буфера). Эти методы всегда возвращают -1, когда больше нет байтов для чтения, а не выбрасывается исключение. Поэтому их не нужно синхронизировать.
Вы заметите, что read() является абстрактным. И реализующие классы задают "синхронизированный", когда они реализуют этот метод.
Другими словами, абстрактный класс InputStream может обрабатывать многопотоки, а также классы реализации.