Подтвердить что ты не робот

Синхронизированные методы в потоках ввода-вывода 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");
}

Почему эти два метода синхронизируются, а все остальные не являются?

4b9b3361

Ответ 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 может обрабатывать многопотоки, а также классы реализации.