Java协程编程之Loom

 更新时间:2021年08月27日 10:20:41   作者:Throwable  
这篇文章主要介绍了Java协程编程Loom的方法,需要的朋友请看下文

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级别支持

三个新特性不详细展开,目前只是EA版本,还存在修改的可能性,所以也没必要详细展开。

2、Virtual Thread使用

当前版本Loom项目中协程使用并没有引入一个新的公开的虚拟线程VirtualThread类,虽然真的存在VirtualThread,但这个类使用default修饰符,隐藏在java.lang包中,并且VirtualThreadThread的子类。协程的创建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的详细内容,更多关于Java编程Loom的资料请关注脚本之家其它相关文章!

相关文章

  • Java十分钟精通异常处理机制

    Java十分钟精通异常处理机制

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等
    2022-03-03
  • Java如何在临界区中避免竞态条件

    Java如何在临界区中避免竞态条件

    这篇文章主要介绍了Java如何在临界区中避免竞态条件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • java 遍历Map及Map转化为二维数组的实例

    java 遍历Map及Map转化为二维数组的实例

    这篇文章主要介绍了java 遍历Map及Map转化为二维数组的实例的相关资料,希望通过本文能帮助到大家,实现这样的功能,需要的朋友可以参考下
    2017-08-08
  • Java源码跟踪阅读技巧【值得收藏】

    Java源码跟踪阅读技巧【值得收藏】

    今天跟大家分享一下我平时阅读源码的几个小技巧,对于阅读Java中间件如Spring、Dubbo等框架源码的同学有一定帮助。本文基于Eclipse IDE,感兴趣的朋友跟随小编一起看看吧
    2018-11-11
  • SpringBoot生产环境打包如何去除无用依赖

    SpringBoot生产环境打包如何去除无用依赖

    这篇文章主要介绍了SpringBoot生产环境打包如何去除无用依赖问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Java实现Word/Excel/TXT转PDF的方法

    Java实现Word/Excel/TXT转PDF的方法

    这篇文章主要介绍了Java实现Word/Excel/TXT转PDF的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • Java中的ConcurrentLinkedQueue松散队列解析

    Java中的ConcurrentLinkedQueue松散队列解析

    这篇文章主要介绍了Java中的ConcurrentLinkedQueue松散队列解析,链表是松散的,链表节点并不都是有效的,允许存在无效节点val=null,但是只有最后一个节点才能next=null,需要的朋友可以参考下
    2023-12-12
  • Java持久化框架Hibernate与Mybatis优劣及选择详解

    Java持久化框架Hibernate与Mybatis优劣及选择详解

    这篇文章主要介绍了Java持久化框架Hibernate与Mybatis优劣及选择详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Java填充替换数组元素实例详解

    Java填充替换数组元素实例详解

    这篇文章主要通过两个实例说明Java填充和替换数组中元素的方法,需要的朋友可以参考下。
    2017-08-08
  • Java设计模式之外观模式示例详解

    Java设计模式之外观模式示例详解

    外观模式为多个复杂的子系统,提供了一个一致的界面,使得调用端只和这个接口发生调用,而无须关系这个子系统内部的细节。本文将通过示例详细为大家讲解一下外观模式,需要的可以参考一下
    2022-08-08

最新评论