java// 定义一个 runnable
private static Runnable runnable = () -> {
try {
Thread thread = Thread.currentThread();
System.out.printf("name: %s, isVirtual: %b, isDaemon: %b%n", thread.getName(), thread.isVirtual(), thread.isDaemon());
Thread.sleep(Duration.ofSeconds(3));
} catch (Exception ignored) {
}
};
// 通过 Thread 启动虚拟线程和平台线程
public static void thread() {
// Thread.ofVirtual()
Thread thread1 = Thread.ofVirtual().name("thread-1").unstarted(runnable);
Thread thread2 = Thread.ofPlatform().name("thread-2").unstarted(runnable);
thread1.start();
thread2.start();
Thread.ofVirtual().name("thread-3").start(runnable);
Thread.ofPlatform().name("thread-4").start(runnable);
// ThreadFactory
ThreadFactory factory = Thread.ofVirtual().name("thread-v-", 10L).factory();
factory.newThread(runnable).start();
factory.newThread(runnable).start();
ThreadFactory factory2 = Thread.ofPlatform().name("thread-p-", 10L).factory();
factory2.newThread(runnable).start();
factory2.newThread(runnable).start();
// Thread.startVirtualThread()
Thread.startVirtualThread(runnable).setName("startVirtualThread");
}
// 输出结果
/*
name: thread-2, isVirtual: false, isDaemon: false
name: thread-4, isVirtual: false, isDaemon: false
name: thread-3, isVirtual: true, isDaemon: true
name: thread-1, isVirtual: true, isDaemon: true
name: thread-v-10, isVirtual: true, isDaemon: true
name: thread-v-11, isVirtual: true, isDaemon: true
name: startVirtualThread, isVirtual: true, isDaemon: true
name: thread-p-10, isVirtual: false, isDaemon: false
name: thread-p-11, isVirtual: false, isDaemon: false
*/
javapublic static void executor() {
AtomicInteger temp = new AtomicInteger(0);
long start = System.currentTimeMillis();
// java19后,把线程也实现了 AutoCloseable, 实现自动关闭的功能
// 通过 newVirtualThreadPerTaskExecutor 创建虚拟线程池, 本质上是构建一个虚拟线程 ThreadFactory
try(ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 对线程池提交 100 个任务(对变量 + 1)
IntStream.range(0, 100).forEach(i -> {
executor.submit(() -> {
temp.addAndGet(1);
Thread.sleep(Duration.ofSeconds(3));
return i;
});
});
System.out.println(temp.get());
}
System.out.println(temp.get());
long end = System.currentTimeMillis();
System.out.printf("start: %s, end: %s, Millis: %s , Seconds: %s", start, end, end-start, (end-start)/1000);
}
// 输出
/*
2
100
start: 1695795744503, end: 1695795747525, Millis: 3022 , Seconds: 3
*/
javapublic static void compare() throws InterruptedException {
AtomicInteger temp = new AtomicInteger(0);
int cnt = 1_000_000;
CountDownLatch countDownLatch = new CountDownLatch(cnt);
Runnable add = () -> {
temp.addAndGet(1);
countDownLatch.countDown();
};
ThreadFactory factory = Thread.ofPlatform().name("thread-p-", 1L).factory();
//ThreadFactory factory = Thread.ofVirtual().name("thread-v-", 1L).factory();
long start = System.currentTimeMillis();
for (int i = 0; i < cnt; i++) {
factory.newThread(add).start();
}
countDownLatch.await();
System.out.println(temp.get());
long end = System.currentTimeMillis();
System.out.printf("start: %s, end: %s, Millis: %s , Seconds: %s", start, end, end-start, (end-start)/1000);
}
/*
---------- platform 输出:
1000000
start: 1695818491677, end: 1695818568130, Millis: 76453 , Seconds: 76
Process finished with exit code 0
---------- virtual 输出:
1000000
start: 1695819481901, end: 1695819482755, Millis: 854 , Seconds: 0
Process finished with exit code 0
*/
在传统的 java 程序中,线程是Java的并发单位 , 应用服务器通常来讲,会通过一个请求一个线程的方式来处理彼此独立的并发用户请求。通过 利特尔法则 约束,可以得出吞吐量和并发量成正比,即 吞吐量 = 并发了 × (一秒 ÷ 延迟时间) 。。比如一个程序的处理延迟时间为 100ms,并发数量为10,那么 吞吐量 = 10 * (1 / 0.1) = 100 。所以 线程数量必须随着吞吐量的增长而增长 。然而,可用线程的数量是有限的。即使使用池化技术,也只是减少线程创建和销毁的开销,并发量并不会增加。
在这个过程中,出现了每个请求线程的风格,转而采用线程共享风格。比如 Reactor 模式,简单说下就是请求不是从头到尾在一个线程上处理,而是在等待另一 I/O 操作完成时将其线程返回到池中,在 netty#线程模型概述 中提到,netty 就是通过主从 Reactor 进行设计的,这里就不赘述了。在线程共享风格中,存在几个问题:堆栈跟踪不提供可用的上下文;调试器无法单步执行请求处理逻辑;编程风格与 Java 平台不一致;并发单位不是线程了
为了在提高并发量的同时统一平台风格,java 推出了 虚拟线程 ,使用虚拟线程保留每个请求的线程风格。
使用 虚拟线程 , 当在虚拟线程中运行的代码调用阻塞 I/O 操作时.执行非阻塞系统调用,并挂起虚拟线程,直到稍后恢复。
本文作者:Yui_HTT
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
预览: