Java 21,虚拟线程、结构化并发和作用域值( 二 )


四、实践在继续以下探索之前,您需要至少下载JDK 19或直接下载JDK 20 。截止到2023年9月,JDK 20是官方发布的最高版本 。如果使用JDK 19,您将无法体验到Scoped Values功能 。

Java 21,虚拟线程、结构化并发和作用域值

文章插图
1*GQ22_fxZ-eRKk85BBXuHWQ.png
或者直接下载JDK 21的早期访问版本 。
Java 21,虚拟线程、结构化并发和作用域值

文章插图
1*0hHWnZaMVfsKlVCLjMJUSg.png
如果您使用的是IDEA,则您的IDEA版本必须至少为2022.3或更高版本,否则不支持这样的新JDK版本 。
如果您使用的是JDK 19或JDK 20,您应该在项目设置中将语言级别设置为19或20 。否则 , 在编译时会提示您无法使用预览版本功能 。虚拟线程是预览版本的功能 。
Java 21,虚拟线程、结构化并发和作用域值

文章插图
1*6oGVASOHa2kRTtbZ--F7AQ.png
如果您使用的是JDK 21 , 请将语言级别设置为X - 实验性功能 。此外,由于JDK 21不是官方版本,您需要进入IDEA设置(请注意,这是IDEA设置 , 而不是项目设置),并手动将项目的目标字节码版本更改为21 。当前,最高选项为20,即JDK 20 。将其设置为21后,您可以在JDK 21中使用这些功能 。
Java 21,虚拟线程、结构化并发和作用域值

文章插图
1*8ltpmMzUzE4u5CAGgNvIOg.png
1. 虚拟线程现在我们如何启动线程?
首先,声明一个线程类,实现Runnable接口,并实现run方法 。
public class SimpleThread implements Runnable {@Overridepublic void run() {System.out.println("name:" + Thread.currentThread().getName());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}然后,您可以使用这个线程类并启动线程 。
Thread thread = new Thread(new SimpleThread());thread.start();拥有虚拟线程后,如何实现呢?
Thread.ofPlatform().name("thread-test").start(new SimpleThread());以下是使用虚拟线程的几种方式 。
(1) 直接启动虚拟线程
Thread thread = Thread.startVirtualThread(new SimpleThread());(2) 使用ofVirtual() , 构建器模式启动虚拟线程,您可以设置线程名称、优先级、异常处理和其他配置
Thread.ofVirtual().name("thread-test").start(new SimpleThread());或者:
Thread thread = Thread.ofVirtual().name("thread-test").uncaughtExceptionHandler((t, e) -> {System.out.println(t.getName() + e.getMessage());}).unstarted(new SimpleThread());thread.start();(3) 使用工厂创建线程
ThreadFactory factory = Thread.ofVirtual().factory();Thread thread = factory.newThread(new SimpleThread());thread.setName("thread-test");thread.start();(4) 使用Executors
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();Future<?> submit = executorService.submit(new SimpleThread());Object o = submit.get();2. 结构化并发想象以下情景 。假设您有三个任务需要同时执行 。只要任何一个任务完成并返回结果,就可以直接使用该结果,可以停止其他两个任务 。例如,一个天气服务通过三个渠道获取天气情况,只要一个渠道返回即可 。
在这种情况下,在Java 8下应该做什么,当然也是可以的 。
List<Future<?>> futures = executor.invokeAll(tasks);String result = executor.invokeAny(tasks);使用ExecutorService的invokeAll和invokeAny方法实现 , 但会有一些额外的工作 。在获取第一个结果后,您需要手动关闭另一个线程 。
在JDK 21中,可以使用结构化编程来实现 。
ShutdownOnSuccess捕获第一个结果并关闭任务范围以中断未完成的线程并唤醒调用线程 。
一种情况是任何子任务的结果都可以直接使用 , 而无需等待其他未完成任务的结果 。
它定义了获取第一个结果或在所有子任务失败时抛出异常的方法 。
public static void mAIn(String[] args) throws IOException {try (var scope = new StructuredTaskScope.ShutdownOnSuccess()) {Future<String> res1 = scope.fork(() -> runTask(1));Future<String> res2 = scope.fork(() -> runTask(2));Future<String> res3 = scope.fork(() -> runTask(3));scope.join();System.out.println("scope:" + scope.result());} catch (ExecutionException | InterruptedException e) {throw new RuntimeException(e);}}public static String runTask(int i) throws InterruptedException {Thread.sleep(1000);long l = new Random().nextLong();String s = String.valueOf(l);System.out.println(i + "task:" + s);return s;}


推荐阅读