Новости Java #32

• Вышел Kotlin 1.6. Перечислим самые заметные изменения и улучшения:

  • Проверка исчерпываемости в when-стейтментах: если when не покрывает все возможные ветки (например, все подклассы sealed-класса), то компилятор выдаёт предупреждение. В Kotlin 1.7 такие предупреждения станут ошибкой.
  • suspend функциональные типы теперь могут быть суперинтерфейсами (т.е. можно писать, например, class MyClickAction : suspend () -> Unit).
  • Автоматические конверсии из обычных функциональных типов к соответствующим suspend функциональным типам.
  • Улучшенный вывод типов для рекурсивных generic типов.
  • Возможность разработки, используя три предыдущих версии API (т.е. 1.3, 1.4 и 1.5 для 1.6). Раньше поддерживалось только две предыдущих версии API.
  • Поддержка повторяющихся аннотаций с удержанием в рантайме.
  • Появились две новые функции: readln(), которая возвращает non-nullable String, и readlnOrNull().
  • API стали стабильными: typeOf(), Duration, DurationUnit, билдеры коллекций, splitToSequence() с Regex, операции битового поворота чисел.
  • Функцию compareTo() теперь можно вызывать через инфиксную нотацию (x compareTo y).

• Очередных два JEP'а предложены к JDK 18. Это JEP 419: Foreign Function & Memory API (Second Incubator) и JEP 420: Pattern Matching for switch (Second Preview).

Также появился новый JEP 422: Linux/RISC-V Port.

• В проекте Loom появились первые два черновика JEP.

Первый – Structured Concurrency (Preview).

В этом JEP'е предлагается добавить в Java новое API для структурного concurrency. Структурное concurrency – это concurrency, которое заимствует принципы обычного (последовательного) структурного программирования и гарантирует следующее: когда поток выполнения разделяется на несколько потоков выполнения, то эти потоки воссоединяются в том же блоке кода. Все эти потоки логически сгруппированы и организованы в иерархию.

Рассмотрим простой пример кода, выполняющийся последовательно:

String foo() throws IOException, InterruptedException {
    int bar = bar(); // throws IOException, InterruptedException
    String baz = baz(); // то же самое
    return baz + bar;
}

В этом коде нет ничего сверхестественного, и он чрезвычайно прост и надёжен. Но если необходимо выполнить операции bar() и baz() параллельно, то трансформировать этот код с сохранением всех структурных гарантий, используя существующие средства Java, будет не так уж и просто.

Новый предложенный класс StructuredExecutor призван, чтобы решить эту проблему. С его использованием аналогичный распараллеленный код будет выглядеть следующим образом:

String foo() throws IOException, InterruptedException {
    try (var s = StructuredExecutor.open()) {
        var handler = new StructuredExecutor.ShutdownOnFailure();
        Future<Integer> bar = s.fork(() -> bar(), handler);
        Future<String> baz = s.fork(() -> baz(), handler);

        s.join();
        handler.throwIfFailed();

        return baz.resultNow() + bar.resultNow();
    } catch (ExecutionException e) {
        if (e.getCause() instanceof IOException ioe) throw ioe;
        throw new RuntimeException(e);
   }
}

StructuredExecutor в данном случае обеспечивает множество гарантий. Например, если одна из операций bar() и baz() завершается ошибкой, то другая операция отменяется автоматически (если ещё не завершена). Или если операция foo() прерывается в процессе ожидания join(), то обе операции bar() и baz() отменяются. Всех этих гарантий не было бы, если бы использовались существующие реализации ExecutorService. Чтобы эти гарантии обеспечить, пришлось бы для этого написать большое количество дополнительного кода.

Таким образом, новое API значительно облегчит написание чистого и корректного конкурентного кода с использованием структурного подхода.

Второй JEP – это Virtual Threads (Preview).

Виртуальные нити – это новый вид нитей (т.е. новый подкласс java.lang.Thread), которые, в отличие от нитей операционной системы, могут хорошо масштабироваться до миллионов экземпляров. При этом поведение таких нитей практически не отличается от обычных, а значит существующий конкурентный код можно будет смигрировать на виртуальные нити, не затрачивая больших усилий. Виртуальные нити являются пользовательской надстройкой и работают поверх нитей операционной системы, поэтому существуют только для JVM, но не для OS.

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

Вместе с виртуальным нитями также вводится большое количество нового API:

  • Thread.Builder – билдер нитей. Например, виртуальную нить можно создать путём вызова Thread.ofVirtual().name("name").unstarted(runnable).
  • Thread.startVirtualThread(Runnable) – удобный метод, позволяющий создать и сразу же запустить виртуальную нить.
  • Thread.isVirtual().
  • Thread.join(Duration) и Thread.sleep(Duration).
  • Executors.newVirtualThreadExecutor() и Executors.newThreadPerTaskExecutor().
  • Future.join(), Future.state() и Future.isCompletedNormally().
  • ExecutorService теперь является AutoCloseable.
  • И другие.

Для виртуальных нитей также добавляется поддержка в дебаггере, JVM TI и Java Flight Recorder.

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

• Вышел Spring Boot 2.6.

Подписывайтесь на канал в Telegram, чтобы не пропускать новости.

Все материалы на этом сайте выложены под лицензией CC BY-SA 4.0
© Евгений Козлов, 2017-2021
Feed