Разница между потоками Java 8 и наблюдаемыми значениями RxJava

Являются ли потоки Java 8 похожими на наблюдаемые RxJava?

Определение потока Java 8:

Классы в новом пакете java.util.stream предоставляют Stream API для поддерживать функциональные операции над потоками элементов.

Ответ 1

TL; DR: все библиотеки обработки последовательности/потока предлагают очень похожий API для построения конвейера. Различия в API для обработки многопоточности и составления конвейеров.

RxJava довольно сильно отличается от Stream. Из всех JDK-объектов наиболее близким к rx.Observable является, возможно, java.util.stream.Collector Stream + CompletableFuture combo (которое обходится ценой работы с дополнительным монадным слоем, т.е. С обработкой преобразования между Stream<CompletableFuture<T>> и CompletableFuture<Stream<T>>).

Существуют значительные различия между Observable и Stream:

  • Потоки основаны на вытягивании, Observables основаны на push. Это может показаться слишком абстрактным, но оно имеет существенные, весьма конкретные последствия.
  • Поток может быть использован только один раз, Observable может быть подписан много раз
  • Stream#parallel() разбивает последовательность на разделы, Observable#subscribeOn() и Observable#observeOn() - нет; сложно эмулировать поведение Stream#parallel() .parallel() с Observable, у него когда-то был .parallel() но этот метод вызвал столько путаницы, что поддержка .parallel() была перемещена в отдельный репозиторий на github, RxJavaParallel. Подробнее в другом ответе.
  • Stream#parallel() #rallel Stream#parallel() не позволяет указывать пул потоков для использования, в отличие от большинства методов RxJava, принимающих необязательный планировщик. Поскольку все потоковые экземпляры в JVM используют один и тот же пул fork-join, добавление .parallel() может случайно повлиять на поведение в другом модуле вашей программы.
  • В потоках отсутствуют операции, связанные со временем, такие как Observable#interval(), Observable#window() и многие другие; это в основном потому, что потоки на основе пул
  • Потоки предлагают ограниченный набор операций по сравнению с RxJava. Например, в потоках отсутствуют операции отключения (takeWhile(), takeUntil()); Обходной путь с использованием Stream#anyMatch() ограничен: это терминальная операция, поэтому вы не можете использовать его более одного раза для каждого потока
  • Начиная с JDK 8 нет операции Stream # zip, что иногда бывает весьма полезно
  • Потоки сложно создать самостоятельно, Observable может быть создан многими способами. РЕДАКТИРОВАТЬ: Как отмечено в комментариях, есть способы создания Stream. Однако, поскольку нет терминального короткого замыкания, вы, например, не можете легко сгенерировать Поток строк в файле (хотя JDK предоставляет строки Files # lines и BufferedReader # прямо из коробки, и другими подобными сценариями можно управлять, создавая Stream от итератора).
  • Observable предлагает средство управления ресурсами (Observable#using()); вы можете обернуть его потоком ввода-вывода или мьютексом и быть уверенным, что пользователь не забудет освободить ресурс - он будет автоматически удален при прекращении подписки; В Stream есть onClose(Runnable), но вы должны вызывать его вручную или с помощью try-with-resources. Например Вы должны иметь в виду, что Files # lines() должен быть заключен в блок try-with-resources.
  • Observables синхронизируются на всем протяжении (я фактически не проверял, верно ли то же самое для потоков). Это избавляет вас от размышлений о том, являются ли базовые операции поточно-ориентированными (ответ всегда "да", если нет ошибки), но накладные расходы, связанные с параллелизмом, будут присутствовать, независимо от того, нужен ваш код или нет.

Сводка: RxJava значительно отличается от Streams. Реальные альтернативы RxJava - это другие реализации ReactiveStream, например, соответствующая часть Akka.

Обновление Есть хитрость в использовании нестандартного пула ветвления соединения для Stream#parallel, см. Пул пользовательских потоков в параллельном потоке Java 8

Обновление Все вышеперечисленное основано на опыте работы с RxJava 1.x. Теперь, когда RxJava 2.x уже здесь, этот ответ может быть устаревшим.

Ответ 2

Java 8 Stream и RxJava выглядит довольно похоже. Они имеют похожие операторы (фильтр, карта, flatMap...), но не созданы для одного и того же использования.

Вы можете выполнять задачи asynchonus с помощью RxJava.

С потоком Java 8 вы перемещаете элементы своей коллекции.

Вы можете сделать почти то же самое в RxJava (элементы трассировки коллекции), но, поскольку RxJava сосредоточен на параллельной задаче,..., он использует синхронизацию, защелку... Так что одна и та же задача с использованием RxJava может быть медленнее, чем с потоком Java 8.

RxJava можно сравнить с CompletableFuture, но это может вычислить не только одно значение.

Ответ 3

Существует несколько технических и концептуальных различий, например, потоки Java 8 - однопользовательские, основанные на тяге, синхронные последовательности значений, тогда как RxJava Observables являются повторно наблюдаемыми, адаптивно основанными на push-pull, потенциально асинхронными последовательностями значений. RxJava нацелен на Java 6+ и работает на Android.

Ответ 4

Java 8 Streams основаны на pull. Вы перебираете поток Java 8, потребляющий каждый элемент. И это может быть бесконечный поток.

RXJava Observable по умолчанию основан на нажатии. Вы подписываетесь на Наблюдаемый, и вы получите уведомление, когда придет следующий товар (onNext), или когда поток будет завершен (onCompleted), или когда произошла ошибка (onError). Поскольку с Observable вы получаете события onNext, onCompleted, onError, вы можете выполнять некоторые мощные функции, такие как объединение разных Observable в новый (zip, merge, concat). Другие вещи, которые вы могли бы сделать, это кеширование, дросселирование,... И он использует более или менее тот же API на разных языках (RxJava, RX в С#, RxJS,...)

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

Ответ 5

Существующие ответы являются исчерпывающими и правильными, но для начинающих отсутствует четкий пример. Позвольте мне поставить некоторые конкретные термины, такие как "push/pull-based" и "re-observable". Примечание: я ненавижу термин Observable (это поток ради бога), поэтому просто буду ссылаться на потоки J8 против RX.

Рассмотрим список целых чисел,

digits = [1,2,3,4,5]

J8 Stream - это утилита для изменения коллекции. Например, даже цифры могут быть извлечены как,

evens = digits.stream().filter(x -> x%2).collect(Collectors.toList())

Это в основном карта Python , фильтр, сокращение, очень хорошее (и давно назревшее) дополнение к Java. Но что, если цифры не были собраны раньше времени - что, если цифры были переданы во время работы приложения - мы могли бы отфильтровать их даже в режиме реального времени.

Представьте, что отдельный процесс потока выводит целые числа в случайное время, пока приложение работает (--- обозначает время)

digits = 12345---6------7--8--9-10--------11--12

В RX even может реагировать на каждую новую цифру и применять фильтр в режиме реального времени

even = -2-4-----6---------8----10------------12

Там нет необходимости хранить входные и выходные списки. Если вам нужен выходной список, нет проблем, которые тоже можно стримировать. На самом деле все это поток.

evens_stored = even.collect()  

Вот почему такие термины, как "без состояний" и "функционал", больше связаны с RX

Ответ 6

RxJava также тесно связан с инициатива реактивных потоков и рассматривает ее как простую реализацию API реактивных потоков (например, по сравнению с реализация потоков Akka). Основное отличие состоит в том, что реактивные потоки предназначены для обработки обратного давления, но если вы посмотрите на страницу реактивных потоков, вы получите эту идею. Они довольно хорошо описывают свои цели, а потоки также тесно связаны с реактивным манифестом.

В потоках Java 8 реализована неограниченная коллекция, очень похожая на Scala Stream или Clojure lazy seq.

Ответ 7

Java 8 Streams позволяют эффективно обрабатывать действительно большие коллекции, одновременно используя многоядерные архитектуры. Напротив, RxJava по умолчанию имеет однопоточность (без планировщиков). Таким образом, RxJava не будет использовать многоядерные машины, если вы сами не закодируете эту логику.