This is the third day of my participation in the August Text Challenge.More challenges in August

The premise

I have been paying attention to the development progress of the JDK coroutine library for a long time, but I was very busy and rarely checked the content on the official website of OpenJDK. Java coroutines project Loom (because the project is still in development stage, given its official website https://openjdk.java.net/projects/loom is only a small amount of Loom project related information) in 2018 before, Earlier versions of Loom have been published based on JDK17 and JDK18. When I download an earlier version of Loom, I only find the JDK18-compiled version:

The download can be found at jdk.java.net/loom

Because the JDK version is too high, you can use the mainstream IDE import Loom-JDK-18+9 for code highlighting and syntax reminding, temporarily can not find a way to compile, temporarily use the JDK to execute the directory under the javac command script to compile, use Java command script to run.

A brief introduction to Loom project

Loom – Fibers, Continuations and Tail-Calls for the JVM

The title of the Loom project already highlights three new features introduced:

  • Fibers: I saw it a few years agoLoomThe project’s test code is usedFiberthisAPI(Now this oneAPIHas been removed), which stands for lightweight thread, also known as coroutine, also known as lightweight user thread, which is amazing at the momentJDKIs actually calledVirtual Thread(Virtual thread)
  • Continuations“What is the next block of code to be executed?”
  • Tail-CallsTail: callVMLevel of support

The three new features are not detailed. They are currently only EA versions and are subject to change, so there is no need to elaborate on them.

Virtual Thread using

Coroutine use in the current version of Loom project does not introduce a new public VirtualThread class. Although virtualThreads do exist, this class uses the default modifier and is hidden in the java.lang package. VirtualThread is a subclass of Thread. The coroutine creation API is in the Thread class:

Create coroutines using this API as follows:

public static void main(String[] args) {
    Thread fiber = Thread.startVirtualThread(() -> System.out.println("Hello Fiber"));
}
Copy the code

From the current source:

  • VirtualThreadthroughThread.currentThread()Gets the parent thread’s scheduler ifmainMethod, then the parent thread of the coroutine instance in the code above ismainthread
  • The default scheduler is created by the systemForkJoinPoolInstance (VirtualThread.DEFAULT_SCHEDULER), enteredRunnableThe instance is encapsulated asRunContinuation, which is ultimately executed by the scheduler
  • fortimed unpark(blocking, waiting to wake up) coroutine, created using the systemScheduledExecutorServiceInstance to wake up
  • This static factory method runs as soon as the coroutine is created and returns an instance of the coroutine

If, in accordance with the above Thread. StartVirtualThread () method to create coroutines, apparently unable to define coroutines, such as the name of the attribute. The Loom project solves this problem reasonably by introducing the Builder pattern for the Thread class:

// Create a platform Thread builder corresponding to the Thread instance
public static Builder.OfPlatform ofPlatform(a) {
    return new ThreadBuilders.PlatformThreadBuilder();
}

// create a VirtualThread builder corresponding to a VirtualThread
public static Builder.OfVirtual ofVirtual(a) {
    return new ThreadBuilders.VirtualThreadBuilder();
}
Copy the code

To put it simply:

  • ofPlatform()Method used to buildThreadInstance, right herePlatform ThreadPlatform threads are just thatJDK1.0An imported thread instance, a normal user thread
  • ofVirtual()Method used to buildVirtualThreadInstance, that is, build coroutine instances

The chains of all Setter methods for the two builder instances expand as follows:

public static void main(String[] args) {
    Thread.Builder.OfPlatform platformThreadBuilder = Thread.ofPlatform()
            // Whether to daemon the thread
            .daemon(true)
            / / thread group
            .group(Thread.currentThread().getThreadGroup())
            // The thread name
            .name("thread-1")
            Prefix + (start + 1) => prefix + (start + 1) => prefix + (start + 1)
            // start > 0 overrides the name attribute configuration
            .name("thread-".1L)
            // Whether to enable ThreadLocal
            .allowSetThreadLocals(false)
            // Whether to enable InheritableThreadLocal
            .inheritInheritableThreadLocals(false)
            // Set the priority
            .priority(100)
            // Set the thread stack depth
            .stackSize(10)
            // Sets the exception handler not caught
            .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()
            // Coroutine name
            .name("fiber-1")
            // Coroutine name prefix + start increment => prefix + start, the next coroutine name created is prefix + (start + 1)
            // start > 0 overrides the name attribute configuration
            .name("fiber-".1L)
            // Whether to enable ThreadLocal
            .allowSetThreadLocals(false)
            // Whether to enable InheritableThreadLocal
            .inheritInheritableThreadLocals(false)
            // Set the scheduler. Executor instances, where the scheduler is a thread pool, to NULL use VirtualThread.DEFAULT_SCHEDULER
            .scheduler(null)
            // Sets the exception handler not caught
            .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"));
}
Copy the code

One thing you can see here is that the builder is reusable. If you want to use the builder to create threads or coroutines with the same set of parameters, you can set the name(String prefix, long start) method to define the thread or coroutine name prefix with a number greater than or equal to zero. To create threads or coroutines in batches, call Builder#unstarted(Runnable task) repeatedly, with names like prefix + start, prefix + (start + 1), prefix + (start + 2), and so on. Coroutine creation is basically as simple as that, calling the start() method directly to run it:

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();
        // The main thread is asleepThread.sleep(Long.MAX_VALUE); }}Copy the code

Currently, the above classes cannot be compiled in mainstream IDES, so you can only compile and run them using tools in the JDK directory as follows:

#Run - current directory 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
Copy the code

One thing you can see here is that the daemon identity of all coroutine instances defaults to true and cannot be changed.

summary

If you use the Loom project from an early start, you can get a sneak peek into how JVM developers are building on coroutines as a major feature, which can be a great way to increase your interest in learning JDK kernel code. From the current point of view, for the implementation of the coroutine Loom project is estimated to be far from the RELEASE version of many functions need to be improved, including the stability of the new API, and whether the coroutine can be used in the original JUC class library (the current Loom-JDK-18+9 does not modify the original thread pool class library) and other problems need to be solved. So wait while you stay focused.

(e – a – 20210818 – c – 2 – d)