线程不够用怎么办?

简介: ### 并发编程挑战与解决方案概览- 多线程导致线程爆炸,浪费CPU及可能导致JVM崩溃。线程池缓解问题,但仍有阻塞IO的效率低下。- 非阻塞IO(如servlet3.1/Tomcat)和事件驱动(Reactive/Future)减少线程使用,但学习曲线陡峭。- 轻量级线程如Netty、Spring Flux和虚拟线程(Java Loom)提升性能,但普及尚需时日。Java21引入虚拟线程,有望成未来性能关键。

一、同步并发编程困境-线程爆炸

为了提高系统利用率,进程发展到多线程,但是多线程并不完美
image.png

  1. 问题 1:线程等待或线程阻塞。CPU浪费。线程池满而拒绝服务。
  2. 问题 2:创建更多线程可能会导致 JVM 崩溃(内存不足)。每个线程大概需要占用1M内存。

一) 典型线程池应用

image.png

二、技术的发展和探索

一)提高线程利用率:阻塞IO和非阻塞IO

  1. 阻塞IO:image.png
  2. 非阻塞IO:image.png
  3. servlet3.1和tomcat对非阻塞IO的支持:image.png

二)减少线程使用(不阻塞线程):事件驱动, Reactive, Future

  1. 事件驱动:image.png
  2. Reactive响应式编程: image.png
  3. Spring Future异步方法编排: image.png

三)非阻塞NIO和事件驱动结合:Netty和HSF异步,HttpAsyncClient, 异步Tair

  1. Netty:image.png
  2. SpringFlux:image.png
  3. Apache的HttpAsyncClient
  4. Tair异步SDK

四)轻量级线程:纤程(Fiber),协程(coroutine),虚拟线程(VirtualThread)

异步响应式的问题:1. 需要学习一种新的编程模式,学习曲线略高。 2. 调用追踪困难。3. 只要有一个库不支持响应式就会导致响应式失效。

image.png

void handle(Request request, Response response) {
    var url1 = ...
    var url2 = ...

    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var future1 = executor.submit(() -> fetchURL(url1));
        var future2 = executor.submit(() -> fetchURL(url2));
        response.send(future1.get() + future2.get());
    } catch (ExecutionException | InterruptedException e) {
        response.fail(e);
    }
}

String fetchURL(URL url) throws IOException {
    try (var in = url.openStream()) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    }
}

三、总结

随着Java的发展,Java21发布集成了ProjectLoom,Java正式支持了虚拟线程。虚拟线程基本上会成为下一代Java性能爆发的利器。但是目前国内主流的服务器Java版本还停留在Java8版本,而国外基本上也停留在Java11版本。距离大家真正使用上虚拟现成可能还有一段时间。大家可以根据自己的项目环境,结合上面的技术提升现有的项目吞吐能力。

相关文章
|
1月前
|
存储 安全 Java
C++线程浅谈
C++线程浅谈
|
9月前
|
Java C语言 Python
线程那些事
线程那些事
31 0
|
算法 NoSQL Java
02.关于线程你必须知道的8个问题(上)
大家好,我是王有志,欢迎来到《Java面试都问啥?》。 今天我们来一起看看在面试中,关于线程各大公司大都喜欢问哪些问题。
84 1
02.关于线程你必须知道的8个问题(上)
|
Java Linux 调度
03.关于线程你必须知道的8个问题(中)
大家好,我是王有志,欢迎来到《Java面试都问啥?》。我们书接上回,继续聊Java面试中关于线程的问题。
59 1
03.关于线程你必须知道的8个问题(中)
|
缓存 监控 Java
线程
多线程
94 0
|
Java 编译器 Linux
初识 线程
初识 线程
88 0
初识 线程
|
Java 调度
线程小记
线程小记
|
Java
什么是线程
什么是线程
112 0
线程睡眠
Thread.sleep方法会导致当前线程暂停执行一段指定的时间...
|
存储 Linux
线程局部存储
TLS:Thread Local Storage,线程局部存储声明为TLS的变量在每个线程都会有一个副本,各个副本完全独立,每个副本的生命期与线程的生命期一样,即线程创建时创建,线程销毁时销毁。 C++11起可以使用thread_local关键字声明TLS变量,变量可以是任意类型。
2046 0