Java协程编程之Loom
目录
- Java协程编程Loom
- 1、Loom项目简单介绍
- 2、Virtual Thread使用
Java协程编程Loom
前提:
OpenJDK给出的官网https://openjdk.java.net/projects/loom有少量Loom项目相关的信息)JDK17编译和JDK18编译等早期版本,Loom早期版本JDK18编译的版本:下载入口在:https://jdk.java.net/loom
由于该JDK版本过高,目前可以使用主流IDE导入Loom-JDK-18+9进行代码高亮和语法提醒,暂时找不到方法进行编译,暂时使用该JDK执行目录下的的javac命令脚本进行编译,使用java命令脚本运行。
1、Loom项目简单介绍
Loom - Fibers, Continuations and Tail-Calls for the JVM
Loom项目的标题已经凸显了引入的三大新特性:
Fibers:
几年前看过当时的Loom项目的测试代码就是使用Fiber这个API(现在这个API已经被移除),意为轻量级线程,即协程,又称为轻量级用户线程,很神奇的是在目前的JDK中实际上称为Virtual Thread(虚拟线程)Continuations:
直译为"连续",实现上有点像闭包,参考不少资料,尚未准确理解其具体含义,感觉可以"粗暴"解读为"程序接下来要执行什么"或者"下一个要执行的代码块"Tail-Calls:
尾调用VM级别支持
2、Virtual Thread使用
当前版本
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(); }// 创建虚拟线程建造器,对应于VirtualThreadpublic static Builder.OfVirtual ofVirtual() {return new ThreadBuilders.VirtualThreadBuilder(); }
简单说就是:
【Java协程编程之Loom】
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() {@Overridepublic void uncaughtException(Thread t, Throwable e) {}}); // thread-1Thread firstThread = platformThreadBuilder.unstarted(() -> System.out.println("Hello Platform Thread First")); // thread-2Thread 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() {@Overridepublic void uncaughtException(Thread t, Throwable e) {}}); // fiber-1Thread firstFiber = virtualThreadBuilder.unstarted(() -> System.out.println("Hello Platform Virtual First")); // fiber-2Thread 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.execn.throwx.fiber.sample.FiberSample2
这里也看出了一点,所有的协程实例的
daemon
标识默认为true
且不能修改文章图片
以上就是Java协程编程之Loom的详细内容,更多关于Java编程Loom的资料请关注脚本之家其它相关文章!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 使用协程爬取网页,计算网页数据大小
- python青少年编程比赛_第十一届蓝桥杯大赛青少年创意编程组比赛细则
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)