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