Как бэкэнд-инженер, мы сталкиваемся с ситуациями, когда приходится обрабатывать данные асинхронно. Сегодня давайте посмотрим, как это делается в java и разными способами.

Thread

Самый простой, но такой мощный компонент параллелизма в Java — это Thread. Поток Java фактически связан с потоком операционной системы. Самый простой способ создать Thread заключается в его расширении и переопределении run :

Запуск потока вызывает run() быть вызванным.
Вы можете спросить; да, у Thread есть множество других методов, которые можно переопределить:

  • В большинстве случаев мы не хотим переопределять другие методы потока.
  • Как только мы расширим Thread класс, расширяющий класс теряет способность к дальнейшему расширению, поскольку Java не поддерживает множественное наследование.
  • Каждый поток имеет свой собственный объект, когда мы его расширяем, и это плохо для здоровья памяти, когда есть тонны Objects of the extended Thread созданный.

Java решает эти проблемы с помощью интерфейса Runnable. Фактически, Thread имеет перегруженный метод, который принимает Runnable.

Runnable

Runnable это интерфейс, который имеет только один метод: run(). Да, Runnable — это функциональный интерфейс, и его экземпляр можно создать с помощью лямбда-функции. Тем не менее, это простой способ сделать это; для сложных вещей мы могли бы реализовать это. Смотрите разницу здесь. Все дело в требовании:

Хотя у Runnable есть run(), это не Thread, а просто класс Java, пока он не будет передан под контроль Thread. Запуск потока приводит к тому, что исполняемый объект run() быть вызванным.

Да, Java решила эту проблему в версии 1.5, и это Callable.

Callable<V>

Callable является общим интерфейсом. Почему? Запустите эту команду возвращаемого значения в качестве универсального запуска этой команды. Callable — слишком функциональный интерфейс и call() это единственный метод, метод без аргументов, который выдает Exception и возвращает универсальное значение run this command.

Реализация Callable очень похожа на Runnable:

См. здесь, вызов обрабатывает данные и возвращает значение, которое может быть собрано после выполнения. Но есть ли огромная разница в его вызове? Мы используем ExecutorService вызывать и Future задержать результат. Давайте поговорим о том, почему.

Как видите, нет контролируемого поведения при создании и запуске потоков (также Runnable или Callable). Мы можем захотеть контролировать количество одновременно работающих потоков, поскольку каждый из них связан с потоками ОС. Количество запущенных потоков должно быть меньше количества доступных ядер ЦП. Все вместе Java решает эту проблему путем ExecutorService интерфейс.
Выше была информационная статья о Асинхронное программирование на Java: часть I.
Не стесняйтесь обращаться к нам через раздел комментариев, если у вас есть какие-либо вопросы относительно инструкций, которыми мы поделились выше.