JDK 19新特性之 VirtualThread

文章对比了Java中创建普通线程和虚拟线程的两种方式,通过Executors.newFixedThreadPool(1)和Executors.newVirtualThreadPerTaskExecutor()的执行效果,展示了虚拟线程几乎没有间隔地执行任务的特点。分析了虚拟线程的启动原理,指出其利用ForkJoinPool实现并发执行,从而提高了效率。
摘要由CSDN通过智能技术生成

一、创建测试类,对比2种写法的区别

1、自己创建一个类 MyTask实现 Runnable


import java.time.LocalDateTime;

public class MyTask implements  Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread()+"  "+LocalDateTime.now());
        try {
        // 线程睡眠 3s 
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

2、方式1:Executors.newFixedThreadPool(1) 执行

import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VirtualThreadTest {

    public static void main(String[] args) throws InterruptedException {
//      方法1: 使用普通的 Thread
        ExecutorService executorService = Executors.newFixedThreadPool(1);

//        方法2: 使用 VirtualThread
//        ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();

//		一共执行5个任务
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());

        Thread.sleep(Duration.ofSeconds(5));

        executorService.shutdown();
    }
}

在运行时配置参数: -Djdk.virtualThreadScheduler.parallelism=1-Djdk.virtualThreadScheduler.maxPoolSize=1

jdk.virtualThreadScheduler.parallelism : 表示 “可用于调度虚拟线程的平台线程数量。默认情况下,它等于可用处理器的数量。”
jdk.virtualThreadScheduler.maxPoolSize : 表示 “可用于调度程序的平台线程的最大数量。默认情况下为256。”
在这里插入图片描述

输出结果如下:(每个间隔3s,因为线程睡眠时间是3s )
在这里插入图片描述

3、方式2:Executors.newVirtualThreadPerTaskExecutor() 执行


import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VirtualThreadTest {

    public static void main(String[] args) throws InterruptedException {
//      方法1: 使用普通的 Thread
//        ExecutorService executorService = Executors.newFixedThreadPool(1);

//        方法2: 使用 VirtualThread
        ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());
        executorService.execute(new MyTask());

        Thread.sleep(Duration.ofSeconds(5));

        executorService.shutdown();
    }
}

输出结果如下:(几乎每个任务时间没有间隔时间)
在这里插入图片描述

二、分析原理

1、 Executors.newFixedThreadPool(1)

在这里插入图片描述
由于Executors.newFixedThreadPool(1) 创建的线程池是: ThreadPoolExecutor,查看 java.util.concurrent.ThreadPoolExecutor#execute --> java.util.concurrent.ThreadPoolExecutor#addWorker --> java.util.concurrent.ThreadPoolExecutor.Worker#Worker --> java.util.concurrent.Executors.DefaultThreadFactory#newThread

所以 java.util.concurrent.Executors.DefaultThreadFactory#newThread 代码如下:

 public Thread newThread(Runnable r) {
 			// 创建线程 Thread 
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
2、 Executors.newVirtualThreadPerTaskExecutor()

查看下图的的源码,不难发现 Executors.newVirtualThreadPerTaskExecutor() 创建的线程池是 ThreadPerTaskExecutor ,查看 java.util.concurrent.ThreadPerTaskExecutor#execute --> java.util.concurrent.ThreadPerTaskExecutor#start(java.lang.Runnable) --> java.util.concurrent.ThreadPerTaskExecutor#newThread 为:

 private Thread newThread(Runnable task) {
   	 Thread thread = factory.newThread(task); // 使用线程池工厂 VirtualThreadFactory(看下图)调用 newThread()
     if (thread == null)
         throw new RejectedExecutionException();
     return thread;
}

在这里插入图片描述
查看 java.lang.ThreadBuilders.VirtualThreadFactory#newThread 的代码如下:

    private static class VirtualThreadFactory extends BaseThreadFactory {
        private final Executor scheduler;

        VirtualThreadFactory(Executor scheduler,
                             String name,
                             long start,
                             int characteristics,
                             UncaughtExceptionHandler uhe) {
            super(name, start, characteristics, uhe);
            this.scheduler = scheduler;
        }

        @Override
        public Thread newThread(Runnable task) {
            Objects.requireNonNull(task);
            String name = nextThreadName();
            Thread thread = newVirtualThread(scheduler, name, characteristics(), task);
            UncaughtExceptionHandler uhe = uncaughtExceptionHandler();
            if (uhe != null)
                thread.uncaughtExceptionHandler(uhe);
            return thread;
        }
    }

java.lang.ThreadBuilders#newVirtualThread 的代码

    /**
     * Creates a new virtual thread to run the given task.
     */
    static Thread newVirtualThread(Executor scheduler,
                                   String name,
                                   int characteristics,
                                   Runnable task) {
        if (ContinuationSupport.isSupported()) {
        	// 创建 VirtualThread 
            return new VirtualThread(scheduler, name, characteristics, task);
        } else {
            if (scheduler != null)
                throw new UnsupportedOperationException();
            return new BoundVirtualThread(name, characteristics, task);
        }
    }

三、Why VirtualThread ?

查看 java.lang.VirtualThread#start() 方法:

@Override
public void start() {
	start(ThreadContainers.root());
}

@Override
void start(ThreadContainer container) {
	if (!compareAndSetState(NEW, STARTED)) {
		throw new IllegalThreadStateException("Already started");
	}

	// bind thread to container
	setThreadContainer(container);

	// start thread
	boolean started = false;
	container.onStart(this); // may throw
	try {
		// extent locals may be inherited
		inheritExtentLocalBindings(container);

		// submit task to run thread
		submitRunContinuation();
		started = true;
	} finally {
		if (!started) {
			setState(TERMINATED);
			container.onExit(this);
			afterTerminate(/*executed*/ false);
		}
	}
}

查看 java.lang.VirtualThread#submitRunContinuation() 方法

private void submitRunContinuation() {
	submitRunContinuation(false);
}

/**
 * Submits the runContinuation task to the scheduler.
 * @param {@code lazySubmit} to lazy submit
 * @throws RejectedExecutionException
 * @see ForkJoinPool#lazySubmit(ForkJoinTask)
 */
private void submitRunContinuation(boolean lazySubmit) {
	try {
		if (lazySubmit && scheduler instanceof ForkJoinPool pool) {
			pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
		} else {
			scheduler.execute(runContinuation);
		}
	} catch (RejectedExecutionException ree) {
		// record event
		var event = new VirtualThreadSubmitFailedEvent();
		if (event.isEnabled()) {
			event.javaThreadId = threadId();
			event.exceptionMessage = ree.getMessage();
			event.commit();
		}
		throw ree;
	}
}

内部采用了 ForkJoinPool 来执行对应的线程 (小百科:ForkJoinPool 是JDK 7加入的一个线程池类,在java.util.concurrent 包中。Fork/Join 技术是分治算法(Divide-and-Conquer)的并行实现,它是一项可以获得良好的并行性能的简单且高效的设计技术),因此,打印的时间输出是同一个时间,在线程睡眠的时候,依然可以做其他事情。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值