• Вышел 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.