Contents

Redis教程

响应式变成与虚拟线程

平台线程

  • 平台线程是操作系统的一个线程,大概有以下特点:
    • 需要1ms来启动
    • 会被预先分配约2m内存
    • 上下文切换需要0.1ms
  • 内存的分配与操作系统的实现保持一致:
    • 在线程创建时,操作系统在虚拟内存中为线程栈预留空间。
    • 物理内存分配:实际物理内存往往是按需分配的,当访问栈空间时,出发缺页异常,通过页错误(page fault)机制实现
    • 当线程第一次访问其栈空间的某个页面时,操作系统才会为该页面分配物理内存(懒加载方式)
    • 内存的大小制约了cpu的处理请求数量

给定条件:

  • CPU频率:3.5 GHz (3.5 × 10^9 Hz)
  • 每个请求处理耗时:0.1 ms (0.0001 秒)

从纯频率角度计算理论处理能力:

  • 每秒钟CPU可执行的周期数 = 3.5 × 10^9 周期/秒
  • 每个请求需要的CPU周期数 = 0.1 ms × 3.5 × 10^9 Hz = 0.0001 秒 × 3.5 × 10^9 周期/秒 = 3.5 × 10^5 周期
  • 每秒可处理的请求数 = CPU每秒周期数 ÷ 每请求所需周期数 = (3.5 × 10^9) ÷ (3.5 × 10^5) = 10^4 = 10,000 请求/秒

如果内存只有4G,实际上只能创建2048个平台线程,制约了cpu的处理速度

两种解决方案

异步响应式编程

  • 异步响应式编程,让每个线程不是一直阻塞等待,充分利用cpu的速度,阻塞的事情可以异步来处理

然而响应式编程存在一定的问题,考虑如下代码:

1
2
3
4
5
ExecutorService es = ...;
var f1 = es.submit(someService::readImages);
var f2 = es.submit(someService::readLinks);
var page = new Page(f1.get(1, TImeUnit.SECONDS),
f2.get(1, TimeUnit.SECONDS)

上述代码通过异步处理两个方法,实现了整体耗时的下降,但其存在如下问题:

  1. 更多的线程被阻塞(平台线程):上述代码实际上阻塞了三个线程,两个是线程池的线程,一个是平台线程
  2. debug困难:不能定位到哪个线程出现问题。
  3. loose threads:如果f1因为某种原因,触发异常,则会导致f2永远不执行get(),es线程出会有一个线程什么都不做,永远被阻塞,不能将它用于其他任何事情。

虚拟线程

  • 虚拟线程资源占用比平台线程更小
  • 阻塞虚拟线程不会阻塞平台线程
  • 虚拟线程并不能提供比响应式编程更快的代码,但可以尽情编写阻塞式代码
  • 虚拟线程可以解决平台线程被阻塞的问题,因为可以随便阻塞虚拟线程,阻塞后其就被回收
  • 虚拟线程可以解决难以调试的问题,对虚拟线程运行的代码打断点,实际上是可以看到错误堆栈的

参考如下代码:

1
2
Callable<Images> fetchImages = () -> someService.readImages();
var f = Executors.newVirtualThreadPerTaskExeutor().submit(fetchImages);

结构化并发

  • 结构化并发实际创建了一个资源块,资源块中每个都是并行处理
  • 结构化并发块运行完(因为错误/完全运行结束)后,统一释放虚拟线程资源,清理仍在运行的thread,并将他们kill掉
  • 结构化并发可以解决loose thread问题,因为会被杀掉

参考如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
try (var scope = new StructuredTaskScope<>()){
    var imagesSubtask = scope.fork(() -> someService.readImages());
    var linkSubtask = scope.fork(() -> someService.readLinks());
    scope.join();

    var page = new Page(imagesSubtask.get(),linkSubtask.get());

} catch (InterruptedException e) {

}

总结

虚拟线程实际上不能提供比响应式编程更快的速度(除非响应式变成并没有用好),实际上是解决了响应式编程的问题

  • 更轻量级的阻塞处理(避免阻塞平台线程)
  • 更好的debug模式
  • 解决loose thread问题