编辑
2023-09-21
编程
00

目录

1. 虚拟线程
1.1 虚拟线程的使用
1.2 虚拟线程vs平台线程
1.3 问题与目标

1. 虚拟线程

官方文档

1.1 虚拟线程的使用

  • java.lang.Thread
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 */
  • Executors

java
public 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 */

1.2 虚拟线程vs平台线程

java
public 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 */

1.3 问题与目标

  • 一个请求一个线程:线程数限制吞吐量

在传统的 java 程序中,线程是Java的并发单位 , 应用服务器通常来讲,会通过一个请求一个线程的方式来处理彼此独立的并发用户请求。通过 利特尔法则 约束,可以得出吞吐量和并发量成正比,即 吞吐量 = 并发了 × (一秒 ÷ 延迟时间) 。。比如一个程序的处理延迟时间为 100ms,并发数量为10,那么 吞吐量 = 10 * (1 / 0.1) = 100 。所以 线程数量必须随着吞吐量的增长而增长 。然而,可用线程的数量是有限的。即使使用池化技术,也只是减少线程创建和销毁的开销,并发量并不会增加。

  • 共享线程风格:复杂且不通用

在这个过程中,出现了每个请求线程的风格,转而采用线程共享风格。比如 Reactor 模式,简单说下就是请求不是从头到尾在一个线程上处理,而是在等待另一 I/O 操作完成时将其线程返回到池中,在 netty#线程模型概述 中提到,netty 就是通过主从 Reactor 进行设计的,这里就不赘述了。在线程共享风格中,存在几个问题:堆栈跟踪不提供可用的上下文;调试器无法单步执行请求处理逻辑;编程风格与 Java 平台不一致;并发单位不是线程了

  • 虚拟线程:共享线程风格的语言层实现

为了在提高并发量的同时统一平台风格,java 推出了 虚拟线程 ,使用虚拟线程保留每个请求的线程风格。

使用 虚拟线程 , 当在虚拟线程中运行的代码调用阻塞 I/O 操作时.执行非阻塞系统调用,并挂起虚拟线程,直到稍后恢复。

本文作者:Yui_HTT

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.8