一、创建测试类,对比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)的并行实现,它是一项可以获得良好的并行性能的简单且高效的设计技术),因此,打印的时间输出是同一个时间,在线程睡眠的时候,依然可以做其他事情。