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

Java 8 Коллекции параллельной обработки

Я планирую сделать внутреннюю презентацию в своей компании о новых функциях и концепциях на Java 8.

Я хотел бы сосредоточиться на возможностях параллельной обработки новых библиотек коллекции.

Где бы я ни читал о Java 8 и о необходимости использования итераторов более функционального стиля библиотеки коллекций, упоминается, что это поможет использовать многоядерные серверы, которые в настоящее время нормальны. Но очень редко упоминается, как это стало возможным, и является ли это универсальной истиной, не говоря уже о каких-либо тестах на производительность.

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

Я был бы благодарен за некоторые отзывы за следующие пункты (true/false)..

  • Нить - это самая низкая единица планирования в ОС (да, базовый материал, но не все программисты приложений знают это;))

  • Однопоточная программа может работать только по одному ядру за раз. Так, например, в четырехъядерном процессоре 75% CPU не используется.

  • Проблема с нынешним итератором Java-коллекции заключается в том, что он является внешним итератором, и невозможно (по крайней мере, из коробки) распространять массивную итерацию коллекции на несколько потоков. Новые функции библиотеки коллекции позволяют иметь concurrency, не имея необходимости решать проблемы с низким уровнем concurrency

  • Java 8 позволяет использовать расширенную библиотеку коллекции для параллелизации итерации с использованием внутреннего итератора

    Вместо Java 7

    for (Shape s : shapes) {if (s.getColor() == RED)s.setColor(BLUE); }

    мы имеем в Java 8

    shapes.forEach(s -> { if (s.getColor() == RED) s.setColor(BLUE); })

  • Но для того, чтобы parallellize вышеупомянутой итерации, нужно явно использовать метод parallel() Stream API

    private static void printUsingCoolLambda (final List<String> names) { names.parallelStream().forEach(s -> System.out.println(s)); System.out.println("Printed using printUsingCoolLambda"); }

    Но даже тогда нет никакой гарантии, что операция будет выполняться параллельно, поскольку Javadoc из parallelStream() говорит следующее "Возвращает возможно параллельный {@code Stream} с этой коллекцией в качестве ее источника. Это разрешено для этого метода возвращать последовательный поток"

  • В конечном счете, нет гарантии, что все ядра будут использоваться, поскольку планирование потоков не является ответственностью JVM, скорее диктуемой ОС.

изменить

Мне больше всего сложно получить очки 5 и 6. Поскольку в различных блогах Java 8 говорится, что "используйте этот новый параллельный поток(), и вы получите параллельную обработку из коробки (бесплатно, а вы, как программист, освобождаетесь от необходимости беспокоиться об этом)", мой вопрос в одном предложении было бы , это действительно правильно все время?

4b9b3361

Ответ 1

Я был бы благодарен за некоторые отзывы за следующие пункты (Истина/ложь)..

К сожалению, ни один из ответов не является истинным или ложным. Они все "это зависит" или "это сложно".: -)

1: поток является самой низкой единицей планирования в ОС.

Это в основном верно. ОС расписывают потоки, и по большей части поток Java соответствует потоку ОС.

Однако, больше к истории. Я бы посоветовал вам не слишком много думать о потоках. Это очень низкоуровневая конструкция, используемая для структурирования параллельного приложения.

Конечно, можно писать приложения с помощью потоков, но часто предпочтительнее использовать конструкцию более высокого уровня. Одна из таких конструкций - это задача, которая является частью работы приложения. Если вы можете разделить рабочую нагрузку на отдельные задачи, вы можете отправить эти задачи на Executor, который будет управлять планированием задач на потоки, а также создавать и уничтожать потоки. Это материал java.util.concurrent, который входит в Java SE 5.

Другим способом структурирования параллельных приложений является использование данных parallelism. Java SE 7 представила инфраструктуру Fork-Join. Это относится к разветвлению и объединению не потоков, а задач, в частности задач, представляющих рекурсивно-разделяемые части данных. Структура FJ достаточно эффективна для некоторых рабочих нагрузок, но разделение и объединение задач лежит на программиста, и это может быть обременительным.

Новое в Java SE 8 - это API потоков, который поддерживает данные parallelism гораздо более удобным способом.

Я немного экстраполировал ваш вопрос о потоках, но ваши вопросы, казалось, были сосредоточены на потоках, а для parallelism гораздо больше, чем потоков. (Один из моих коллег недавно сказал: "Нити - это ложный Бог".)

2: однопоточная программа может запускаться только по одному ядру за раз. Например, в четырехъядерном процессоре 75% CPU не используется, например.

В основном верно. Если вы рассматриваете только поток приложений, один поток никогда не сможет использовать более 25% четырехъядерного процессора. Однако, если вы рассматриваете поток Java, запущенный в JVM, даже однопоточное Java-приложение, скорее всего, будет работать быстрее в многоядерной системе, чем в одноядерной системе. Причина в том, что потоки службы JVM, такие как сборщик мусора, могут выполняться параллельно с потоком приложения в многоядерной системе, тогда как они должны упредить поток приложений в одноядерной системе.

3: Проблема с существующим итератором сборника Java заключается в том, что он является внешним итератором, и невозможно (по крайней мере, из коробки) распространять массивную итерацию коллекции на несколько потоков. Новые функции библиотеки коллекций позволяют иметь concurrency, не имея необходимости решать проблемы с низким уровнем concurrency.

В основном да. Внешняя итерация и внутренняя итерация - это понятия. Внешняя итерация воплощается в реальном интерфейсе Iterator. Внутренняя итерация может использовать Iterator, простой for-loop, набор задач fork-join или что-то еще.

Это не так много новой библиотеки коллекций, но новый API Streams в Java 8 обеспечит гораздо более удобный способ распространения работы по потокам.

4: Java 8 позволяет использовать расширенную библиотеку коллекции для параллелизации итерации с использованием внутреннего итератора (... shapes.forEach example...)

Закрыть. Опять же, это новая библиотека Streams, а не коллекции, которая обеспечивает удобный parallelism. Нет ничего лучше Collection.parallelForEach. Чтобы обрабатывать элементы коллекции параллельно, вам нужно вытащить из нее параллельный поток. Существует также множество параллельных операций для массивов в классе java.util.Arrays.

5: Но для того, чтобы распараллелить приведенную выше итерацию, нужно явно использовать метод parallel для Stream API... Но даже тогда нет никакой гарантии, что операция будет выполняться параллельно.

Правильно, вам нужно запросить parallelism с помощью метода parallel или parallelStream, в зависимости от того, начинаете ли вы с потока или коллекции.

Относительно никаких гарантий, конечно, в жизни нет никаких гарантий.:-) В конце концов, если вы работаете в одноядерной системе, ничто не может работать параллельно. Другой сценарий - в апплете менеджер безопасности может запретить приложению использовать более одного потока. На практике в большинстве сред, запрашивающих параллельный поток, действительно будет разделять рабочую нагрузку и параллельно запускать задачи. По умолчанию эти задачи выполняются в общем пуле fork-join, который по умолчанию имеет столько потоков, сколько в нем есть ядра. Но кто-то мог установить количество потоков на другой номер или даже на 1, что является одной из причин, по которым сам API не может предоставить никаких гарантий.

6: В конечном счете, нет гарантии, что все ядра будут использоваться, поскольку планирование потоков не является ответственностью JVM, скорее диктуемой ОС.... Поскольку различные блоги Java 8 говорят только, что "используйте этот новый параллельный поток(), и вы получите параллельную обработку из коробки (бесплатно, а вы, как программист, освобождаетесь от необходимости беспокоиться об этом)", мой вопрос в одном предложении было бы , это действительно правильно все время?

Как и выше, никаких гарантий нет. В системе есть много слоев, где все может идти влево. Даже если ваш общий пул FJ имеет столько потоков, сколько есть ядер, нет никаких гарантий, что каждый поток Java имеет свой собственный поток ОС. (В JVM Hotspot я считаю, что это всегда верно, но это зависит от JVM.) В одной и той же системе, конкурирующей с ядрами, могут быть другие процессы - даже другие JVM - так что ваше приложение может не получить столько ядер, сколько Ты бы хотел. В этом смысле JVM находится во власти ОС для планирования потоков для нее.

Я не уверен, откуда появилась эта запись в блоге, но бит о параллельной обработке "бесплатно" и чувства "вы не должны беспокоиться" раздуты. Фактически, это в основном неправильно.

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

Даже если вам удалось избежать этих ошибок, это не тот случай, когда запуск параллельного потока в N-core системе даст вам ускорение в N раз. Это просто не работает. Для небольших рабочих нагрузок преобладают накладные расходы на разделение и объединение параллельных задач, что может привести к тому, что вычисление будет медленнее, чем последовательное. Для больших рабочих нагрузок служебные данные компенсируются параллельным ускорением, но накладные расходы все еще существуют. Количество ускорений также зависит от характера рабочей нагрузки, характеристик расщепления, комковатости данных и т.д. Настройка параллельного приложения - это черное искусство.

Для легко распараллеливаемых рабочих нагрузок, по моему опыту, довольно легко максимизировать двухъядерную систему. Как правило, четырехъядерная система может получить как минимум 3-кратное ускорение. С большим количеством ядер, не так уж сложно получить ускорение 5x-6x, но получение ускорения сверх того требует реальной работы.

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

Я бы не сказал, что Java 8 дает вам parallelism "бесплатно" или "без проблем" или что-то в этом роде. Я бы сказал, что Java 8 дает вам возможность писать параллельные программы гораздо удобнее, чем раньше. Но вам все равно придется работать, чтобы понять это, и вам, вероятно, придется работать, чтобы добиться ускорения, которое вам нужно.

Ответ 2

- это действительно правильно все время?

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

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

Ответ 3

Другие ответы в основном верны. Однако параллельная функция Java8 основана на структуре Fork/Join. Join() там не работает, поэтому он был заменен CountedCompleter. Этот класс также ошибочен, как я писал в этой статье

В зависимости от того, что так часто упоминается в других ответах, здесь также применяется.