您好,登录后才能下订单哦!
这篇文章将为大家详细讲解有关Java协程编程之Loom项目的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
之前很长一段时间关注JDK
协程库的开发进度,但是前一段时间比较忙很少去查看OpenJDK
官网的内容。Java
协程项目Loom
(因为项目还在开发阶段,OpenJDK
给出的官网https://openjdk.java.net/projects/loom
中只有少量Loom
项目相关的信息)已经在2018
年之前立项,目前已经发布过基于JDK17
编译和JDK18
编译等早期版本,笔者在下载Loom
早期版本的时候只找到JDK18
编译的版本:
下载入口在:https://jdk.java.net/loom
由于该JDK
版本过高,目前可以使用主流IDE
导入Loom-JDK-18+9
进行代码高亮和语法提醒,暂时找不到方法进行编译,暂时使用该JDK
执行目录下的的javac
命令脚本进行编译,使用java
命令脚本运行。
Loom - Fibers, Continuations and Tail-Calls for the JVM
Loom
项目的标题已经凸显了引入的三大新特性:
Fibers
:几年前看过当时的Loom
项目的测试代码就是使用Fiber
这个API
(现在这个API
已经被移除),意为轻量级线程,即协程,又称为轻量级用户线程,很神奇的是在目前的JDK
中实际上称为Virtual Thread
(虚拟线程)
Continuations
:直译为"连续",实现上有点像闭包,参考不少资料,尚未准确理解其具体含义,感觉可以"粗暴"解读为"程序接下来要执行什么"或者"下一个要执行的代码块"
Tail-Calls
:尾调用VM
级别支持
三个新特性不详细展开,目前只是EA
版本,还存在修改的可能性,所以也没必要详细展开。
当前版本Loom
项目中协程使用并没有引入一个新的公开的虚拟线程VirtualThread
类,虽然真的存在VirtualThread
,但这个类使用default
修饰符,隐藏在java.lang
包中,并且VirtualThread
是Thread
的子类。协程的创建API
位于Thread
类中:
使用此API
创建协程如下:
public static void main(String[] args) { Thread fiber = Thread.startVirtualThread(() -> System.out.println("Hello Fiber")); }
从当前的源码可知:
VirtualThread
会通过Thread.currentThread()
获取父线程的调度器,如果在main
方法运行,那么上面代码中的协程实例的父线程就是main
线程
默认的调度器为系统创建的ForkJoinPool
实例(VirtualThread.DEFAULT_SCHEDULER
),输入的Runnable
实例会被封装为RunContinuation
,最终由调度器执行
对于timed unpark
(正在阻塞,等待唤醒)的协程,使用系统创建的ScheduledExecutorService
实例进行唤醒
这个静态工厂方法创建完协程马上运行,返回的是协程实例
如果按照上面的Thread.startVirtualThread()
方法去创建协程,显然无法定义协程的名称等属性。Loom
项目为Thread
类引入了建造者模式,比较合理地解决了这个问题:
// 创建平台线程建造器,对应于Thread实例 public static Builder.OfPlatform ofPlatform() { return new ThreadBuilders.PlatformThreadBuilder(); } // 创建虚拟线程建造器,对应于VirtualThread public static Builder.OfVirtual ofVirtual() { return new ThreadBuilders.VirtualThreadBuilder(); }
简单说就是:
ofPlatform()
方法用于构建Thread
实例,这里的Platform Thread
(平台线程)其实就是JDK1.0
引入的线程实例,普通的用户线程
ofVirtual()
方法用于构建VirtualThread
实例,也就是构建协程实例
这两个建造器实例的所有Setter
方法链展开如下:
public static void main(String[] args) { Thread.Builder.OfPlatform platformThreadBuilder = Thread.ofPlatform() // 是否守护线程 .daemon(true) // 线程组 .group(Thread.currentThread().getThreadGroup()) // 线程名称 .name("thread-1") // 线程名称前缀 + 起始自增数字 => prefix + start,下一个创建的线程名称就是prefix + (start + 1) // start > 0的情况下会覆盖name属性配置 .name("thread-", 1L) // 是否启用ThreadLocal .allowSetThreadLocals(false) // 是否启用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 设置优先级 .priority(100) // 设置线程栈深度 .stackSize(10) // 设置未捕获异常处理器 .uncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { } }); // thread-1 Thread firstThread = platformThreadBuilder.unstarted(() -> System.out.println("Hello Platform Thread First")); // thread-2 Thread secondThread = platformThreadBuilder.unstarted(() -> System.out.println("Hello Platform Thread Second")); Thread.Builder.OfVirtual virtualThreadBuilder = Thread.ofVirtual() // 协程名称 .name("fiber-1") // 协程名称前缀 + 起始自增数字 => prefix + start,下一个创建的协程名称就是prefix + (start + 1) // start > 0的情况下会覆盖name属性配置 .name("fiber-", 1L) // 是否启用ThreadLocal .allowSetThreadLocals(false) // 是否启用InheritableThreadLocal .inheritInheritableThreadLocals(false) // 设置调度器,Executor实例,也就是调度器是一个线程池,设置为NULL会使用VirtualThread.DEFAULT_SCHEDULER .scheduler(null) // 设置未捕获异常处理器 .uncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { } }); // fiber-1 Thread firstFiber = virtualThreadBuilder.unstarted(() -> System.out.println("Hello Platform Virtual First")); // fiber-2 Thread secondFiber = virtualThreadBuilder.unstarted(() -> System.out.println("Hello Platform Virtual Second")); }
这里可以发现一点,就是建造器是可以复用的。如果想用建造器创建同一批参数设置相同的线程或者协程,可以设置name(String prefix, long start)
方法,定义线程或者协程的名称前缀和一个大于等于0
的数字,反复调用Builder#unstarted(Runnable task)
方法就能批量创建线程或者协程,名称就设置为prefix + start
、prefix + (start + 1)
、prefix + (start + 2)
以此类推。协程创建基本就是这么简单,运行的话直接调用start()
方法:
public class FiberSample2 { public static void main(String[] args) throws Exception { Thread.ofVirtual() .name("fiber-1") .allowSetThreadLocals(false) .inheritInheritableThreadLocals(false) .unstarted(() -> { Thread fiber = Thread.currentThread(); System.out.printf("[%s,daemon:%s,virtual:%s] - Hello World\n", fiber.getName(), fiber.isDaemon(), fiber.isVirtual()); }).start(); // 主线程休眠 Thread.sleep(Long.MAX_VALUE); } }
目前无法在主流IDE
编译上面的类,所以只能使用该JDK
目录下的工具编译和运行,具体如下:
# 执行 - 当前目录I:\J-Projects\framework-source-code\fiber-sample\src\main\java (1)编译:I:\Environment\Java\jdk-18-loom\bin\javac.exe I:\J-Projects\framework-source-code\fiber-sample\src\main\java\cn\throwx\fiber\sample\FiberSample2.java (2)执行main方法:I:\Environment\Java\jdk-18-loom\bin\java.exe cn.throwx.fiber.sample.FiberSample2
这里也看出了一点,所有的协程实例的daemon
标识默认为true
且不能修改。
关于“Java协程编程之Loom项目的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。