Статьи

Java 8 пятница, вкусности: Lean Concurrency

В Data Geekery мы любим Java. И так как мы действительно входим в свободный API jOOQ и запросы DSL , мы абсолютно взволнованы тем, что Java 8 принесет в нашу экосистему. Мы пару раз писали о приятных вкусностях Java 8 , и теперь мы чувствуем, что пришло время начать новую серию блогов,…

Ява 8 Пятница

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

Java 8 Goodie: Lean Concurrency

Кто-то однажды сказал, что (к сожалению, у нас больше нет источника):

Младшие программисты считают параллелизм трудным.
Опытные программисты считают параллелизм легким.
Старшие программисты считают параллелизм трудным.

Это совершенно верно. Но с другой стороны, Java 8, по крайней мере, улучшит ситуацию, упростив написание параллельного кода с помощью лямбд и множества улучшенных API. Давайте ближе посмотрим:

Java 8 улучшается на JDK 1.0 API

java.lang.Thread была с самого начала в JDK 1.0. То же java.lang.Runnable имеет java.lang.Runnable , который будет аннотирован с помощью FunctionalInterface в Java 8.

Это почти не представляет сложности, как мы можем наконец представить Runnable s в Runnable с этого Runnable . Давайте предположим, что у нас есть длительная операция:

1
2
3
4
5
6
7
public static int longOperation() {
    System.out.println("Running on thread #"
       + Thread.currentThread().getId());
 
    // [...]
    return 42;
}

Затем мы можем передать эту операцию Threads различными способами, например,

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Thread[] threads = {
 
    // Pass a lambda to a thread
    new Thread(() -> {
        longOperation();
    }),
 
    // Pass a method reference to a thread
    new Thread(ThreadGoodies::longOperation)
};
 
// Start all threads
Arrays.stream(threads).forEach(Thread::start);
 
// Join all threads
Arrays.stream(threads).forEach(t -> {
    try { t.join(); }
    catch (InterruptedException ignore) {}
});

Как мы упоминали в нашем предыдущем посте в блоге, обидно, что лямбда-выражения не нашли простой способ обойти проверенные исключения. Ни один из недавно добавленных функциональных интерфейсов в пакете java.util.function позволяет генерировать проверенные исключения, оставляя работу до call-сайта.
jool-логотип-черный
Таким образом, в нашем последнем посте мы опубликовали jOOλ (также jOOL, jOO-Lambda) , который оборачивает каждый из функциональных интерфейсов JDK в эквивалентный функциональный интерфейс, который позволяет создавать проверенные исключения. Это особенно полезно со старыми API JDK, такими как JDBC, или вышеупомянутым API-интерфейсом Thread. С помощью jOOλ мы можем написать:

1
2
3
4
// Join all threads
Arrays.stream(threads).forEach(Unchecked.consumer(
    t -> t.join()
));

Java 8 улучшается на Java 5 API

Многопоточные API Java были довольно бездействующими вплоть до выпуска потрясающего ExecutorService в Java 5. Управление потоками было обременительным, и людям требовались внешние библиотеки или J2EE / JEE-контейнер для управления пулами потоков. Это стало намного проще с Java 5. Теперь мы можем отправить Runnable или Callable в ExecutorService , который управляет своим собственным пулом потоков.

Вот пример того, как мы можем использовать эти API параллелизма Java 5 в Java 8:

01
02
03
04
05
06
07
08
09
10
11
ExecutorService service = Executors
    .newFixedThreadPool(5);
 
Future[] answers = {
    service.submit(() -> longOperation()),
    service.submit(ThreadGoodies::longOperation)
};
 
Arrays.stream(answers).forEach(Unchecked.consumer(
    f -> System.out.println(f.get())
));

Обратите внимание, как мы снова используем UncheckedConsumer из jOOλ, чтобы обернуть проверенное исключение, выброшенное из вызова get() в RuntimeException .

Параллелизм и ForkJoinPool в Java 8

Теперь API-интерфейс Java 8 Streams многое меняет с точки зрения параллелизма и параллелизма. Например, в Java 8 вы можете написать следующее:

1
2
3
4
Arrays.stream(new int[]{ 1, 2, 3, 4, 5, 6 })
      .parallel()
      .max()
      .ifPresent(System.out::println);

Хотя в данном конкретном случае это и не нужно, все же интересно видеть, что простой вызов функции parallel() запустит операцию IntStream.max () на всех доступных потоках внутреннего ForkJoinPool вашего JDK, не беспокоясь о участвует ForkJoinTasks . Это может быть действительно полезным, так как не все приветствовали API-интерфейс ForkJoin JDK 7 из-за его сложности .

Узнайте больше о параллельных потоках Java 8 в этой интересной статье InfoQ .

Подробнее о Java 8

Параллелизм был одной из главных движущих сил нового Streams API. Возможность установить флаг Stream parallel() в Stream просто замечательна во многих ситуациях.

В последнем примере мы видели метод OptionalInt.ifPresent (), который принимает аргумент IntConsumer для выполнения в случае успешного выполнения предыдущей операции сокращения.

Другие языки, такие как Scala, знают тип «Option» для улучшения обработки NULL. Ранее мы писали о Optional и будем повторять тип Java 8 Optional в контексте потоков Java 8, так что следите за обновлениями!

А пока взгляните на удивительную страницу ресурсов Java 8 от Eugen Paraschiv.

Ссылка: Пятничные вкусности Java 8: Lean Concurrency от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ .