Java协程编程之Loom项目尝鲜
前提
之前很长一段时间关注JDK
协程库的开发进度,但是前一段时间比较忙很少去查看OpenJDK
官网的内容。Java
协程项目Loom
(因为项目还在开发阶段,OpenJDK
给出的官网https://openjdk.java.net/projects/loom
中只有少量Loom
项目相关的信息)已经在2018
年之前立项,目前已经发布过基于JDK17
编译和JDK18
编译等早期版本,笔者在下载Loom
早期版本的时候只找到JDK18
编译的版本:
由于该JDK
版本过高,目前可以使用主流IDE
导入Loom-JDK-18+9
进行代码高亮和语法提醒,暂时找不到方法进行编译,暂时使用该JDK
执行目录下的的javac
命令脚本进行编译,使用java
命令脚本运行。
Loom项目简单介绍
Loom - Fibers, Continuations and Tail-Calls for the JVM
Loom
项目的标题已经凸显了引入的三大新特性:
Fibers
:几年前看过当时的Loom
项目的测试代码就是使用Fiber
这个API
(现在这个API
已经被移除),意为轻量级线程,即协程,又称为轻量级用户线程,很神奇的是在目前的JDK
中实际上称为Virtual Thread
(虚拟线程)Continuations
:直译为"连续",实现上有点像闭包,参考不少资料,尚未准确理解其具体含义,感觉可以"粗暴"解读为"程序接下来要执行什么"或者"下一个要执行的代码块"Tail-Calls
:尾调用VM
级别支持
三个新特性不详细展开,目前只是EA
版本,还存在修改的可能性,所以也没必要详细展开。
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();
}
// 创建虚拟线程建造器,对应于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
且不能修改。
小结
如果用尝鲜的角度去使用Loom
项目,可以提前窥探JVM
开发者们是如何基于协程这个重大特性进行开发的,这对于提高学习JDK
内核代码的兴趣有不少帮助。从目前来看,对于协程的实现Loom
项目距离RELEASE
版本估计还有不少功能需要完善,包括新增API
的稳定性,以及协程是否能够移植到原有的JUC
类库中使用(当前的Loom-JDK-18+9
没有对原来的线程池等类库进行修改)等问题需要解决,所以在保持关注的过程中静心等待吧。
(e-a-20210818 c-2-d)
Java协程编程之Loom项目尝鲜的更多相关文章
- Java协程实践指南(一)
一. 协程产生的背景 说起协程,大多数人的第一印象可能就是GoLang,这也是Go语言非常吸引人的地方之一,它内建的并发支持.Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSP(C ...
- 【Java】网络编程之NIO
简单记录 慕课网-解锁网络编程之NIO的前世今生 & 一站式学习Java网络编程 全面理解BIO/NIO/AIO 内容概览 文章目录 1.[了解] NIO网络编程模型 1.1.NIO简介 1. ...
- JAVA协程 纤程 与Quasar 框架
ava使用的是系统级线程,也就是说,每次调用new Thread(....).run(),都会在系统层面建立一个新的线程,然鹅新建线程的开销是很大的(每个线程默认情况下会占用1MB的内存空间,当然你愿 ...
- java 协程
协程是比线程更轻量级的程序处理单元,也可以说是运行在线程上的线程,由自己控制 1.适用于被阻塞的,且需要大量并发的场景. 2.不适用于,大量计算的多线程,遇到此种情况,更好实用线程去解决. 虽然Jav ...
- Java:网络编程之UDP的使用
java.net 类 DatagramSocket 此类表示用来发送和接收数据报包的套接字,数据报套接字是包投递服务的发送或接收点. java.net 类 DatagramPacket 此类表示数 ...
- Java:网络编程之IP、URL
java.net 类 InetAddress 此类表示互联网协议 (IP) 地址. 会抛出异常 UnknownHostException 直接已知子类: Inet4Address ...
- Java 多线程并发编程之 Synchronized 关键字
synchronized 关键字解析 同步锁依赖于对象,每个对象都有一个同步锁. 现有一成员变量 Test,当线程 A 调用 Test 的 synchronized 方法,线程 A 获得 Test 的 ...
- java 协程框架quasar gradle配置
https://github.com/puniverse/quasar-gradle-template/blob/master/gradle/agent.gradle 1.将其中的"-jav ...
- java 协程框架kilim
http://phl.iteye.com/blog/2247112 http://chen-tao.github.io/2015/10/02/kilim-work-way/ 待丰富
随机推荐
- PHP实现的解汉诺塔问题算法示例
问题描述: 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏.该游戏是在一块铜板装置上,有三根杆(编号A.B.C),在A杆自下而上.由大到小按顺序放置64个金盘(如下图).游戏的目标:把A杆 ...
- Leetcode No.122 Best Time to Buy and Sell Stock II Easy(c++实现)
1. 题目 1.1 英文题目 You are given an array prices where prices[i] is the price of a given stock on the it ...
- excle名字后面直接跟别的出来
名字后面直接跟别的出来 =IF($E6="","",VLOOKUP(E6,通讯录!$B$2:$D$1000,3,0)) $E6:是填写位置的地方 VLOOKUP ...
- Selenium启动Firefox示例(python版)
目前做selenium自动化使用的主流语言分为java和python,前一篇为java版,本篇介绍python实现selenium启动Firefox. 1 #-*- coding:utf-8 -*- ...
- Linux groupadd and groupmod
groupadd [选项] group 三个参数: -g,--gid 指定组gid,除非使用-o,否则gid必须时唯一的 -o,--non-unique 允许创建有重复gid的组 -r, --syst ...
- 【Java数据结构与算法】简单排序、二分查找和异或运算
简单排序 选择排序 概念 首先,找到数组中最小的那个元素,其次,把它和数组的第一个元素交换位置(如果第一个元素就是最小的元素那么它就和自己交换).再次,在剩下的元素中找到最小的元素,将它与数组的第二个 ...
- 『与善仁』Appium基础 — 4、常用ADB命令(一)
目录 1.启动和关闭ADB服务 2.查看ADB版本 3.指定adb server的网络端口 4.查询已连接设备/模拟器 5.获取安卓系统版本 6.为命令指定目标设备 7.发送文件到手机 8.从手机拉取 ...
- Linux从入门到进阶全集——【第十五集:安装apache服务器】
1,查看是否安装了httpd软件包以及其依赖:rpm -qa httpd(rpm -qa | grep httpd),如果没有输出任何信息,表示你没有安装httpd软件包,如果有输出一般是已经安装了: ...
- 简单快速安装Apache+PHP+MySql服务环境(二)—— centos使用yum安装php5.6
使用默认方式yum install php安装的php版本为5.4,在使用某些PHP框架的时候可能会有问题,所以需要安装高版本的PHP. 通过yum list php*查看是否有自己需要安装的版本,如 ...
- 【强连通分量】Proving Equivalences
[题目链接]hdu-2767 [题目描述] Consider the following exercise, found in a generic linear algebra textbook. L ...