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

Поддерживает ли Stream.forEce порядок столкновений последовательных потоков?

Javadoc для Stream.forEach говорит (акцент мой):

Поведение этой операции явно недетерминировано. Для параллельных поточных конвейеров эта операция не гарантирует уважения порядка встречи потока, так как это принесет жертву преимущества параллелизма. Для любого данного элемента действие может выполняться в любое время и в любом потоке, который выбирает библиотека. Если действие обращается к общему состоянию, оно отвечает за предоставление необходимой синхронизации.

Тот же текст присутствует в Java 9 Early Access Javadoc.

Первое предложение ("явно недетерминированное") предлагает (но явно не говорит), что этот порядок не сохраняется. Но следующее предложение, которое явно говорит, что порядок не сохраняется, обусловлен "для параллельных потоковых конвейеров", и это условие было бы лишним, если бы предложение применялось независимо от параллелизма. Это оставляет мне неуверенность в том, что forEach сохраняет порядок поиска для последовательных потоков.

В этом ответе указывается место, где реализация библиотеки потоков вызывает .sequential().forEach(downstream). Это предполагает, что forEach предназначен для сохранения порядка последовательных потоков, но также может быть просто ошибкой в библиотеке.

Я обошел эту двусмысленность в своем собственном коде, используя forEachOrdered чтобы быть в безопасности, но сегодня я обнаружил, что в среде IDE NetBeans "использовать функциональные операции" hint будет конвертировать

for (Foo foo : collection)
    foo.bar();

в

collection.stream().forEach((foo) -> {
    foo.bar();
});

который вводит ошибку, если forEach не сохраняет порядок встреч. Прежде чем сообщать об ошибке с NetBeans, я хочу знать, что на самом деле гарантирует библиотека, подкрепленная источником.

Я ищу ответ из авторитетных источников. Это может быть явный комментарий в реализации библиотеки, обсуждение списков рассылки разработки Java (Google ничего не нашел для меня, но, может быть, я не знаю волшебных слов), или выражение разработчиков библиотеки (из которых я знаю два, Брайан Гетц и Стюарт Маркс, активны в переполнении стека). (Пожалуйста, не отвечайте "просто используйте forEachOrdered вместо" - я уже это делаю, но я хочу знать, не ошибается ли код).

4b9b3361

Ответ 1

Существуют спецификации для описания минимальных гарантий, на которые может зависеть вызывающий, а не на описание реализации. Этот разрыв имеет решающее значение, поскольку он позволяет гибкости реализации развиваться. (Спецификация является декларативной, реализация является обязательной.) Overspecification так же плохо, как underspecification.

Когда спецификация говорит, что "не сохраняет свойство X", это не означает, что свойство X никогда не может наблюдаться; это означает, что реализация не обязана ее сохранять. Ваше заявленное имплицирование, что встречающий порядок никогда не сохраняется, - просто неверный вывод. (HashSet не обещает, что итерация его элементов сохраняет порядок, в который они были вставлены, но это не означает, что это не может случайно произойти - вы просто не можете рассчитывать на это.)

Аналогично, ваш вывод о том, что "предложение, предлагаемое forEach, предназначено для сохранения порядка последовательных потоков", потому что вы видели реализацию, которая делает это в некоторых случаях одинаково неправильной.

В обоих случаях кажется, что вам просто неудобно в том, что спецификация дает forEach большую свободу. В частности, у него есть свобода не сохранять порядок встреч для последовательных потоков, даже если это то, что в настоящее время выполняет реализация, и, кроме того, что трудно представить себе реализацию, выходящую из его пути для обработки последовательных источников из строя. Но это то, что говорит специфика, и то, что он хотел сказать.

Тем не менее, формулировка комментария о параллельных потоках потенциально сбивает с толку, потому что это все еще возможно неверно истолковать. Намерение назвать здесь параллельный случай было педагогическим; спецификация по-прежнему совершенно понятна, поскольку это предложение полностью удалено. Тем не менее, для читателя, который не знает о parallelism, было бы почти невозможно не предполагать, что forEach сохранит порядок встреч, поэтому это предложение было добавлено, чтобы помочь прояснить мотивацию. Но, как вы отмечаете, желание относиться к последовательному делу специально все еще настолько мощно, что было бы полезно прояснить далее.