阜阳哪里做网站,网站游戏制作开发,wordpress主题 个人博客,提高网站访问量什么是虚拟线程
虚拟线程是一种轻量级线程#xff0c;也可以称为协程。它是一种抽象的概念#xff0c;可以理解为在程序中同时执行多个线程的并发执行。虚拟线程是由Java虚拟机#xff08;JVM#xff09;来实现的#xff0c;它并不与特定的操作系统线程绑定#xff0c;而…什么是虚拟线程
虚拟线程是一种轻量级线程也可以称为协程。它是一种抽象的概念可以理解为在程序中同时执行多个线程的并发执行。虚拟线程是由Java虚拟机JVM来实现的它并不与特定的操作系统线程绑定而是通过虚拟线程的调度来实现并发执行。虚拟线程的调度由JVM负责与平台线程有很大的不同。虚拟线程的状态管理、任务提交、休眠和唤醒等也是完全由JVM实现。虚拟线程适用于运行大部分时间被阻塞的任务这些任务通常需要等待I/O操作的完成例如网络请求和数据库查询等。虚拟线程与平台线程不同通常只有一个浅调用堆栈例如只执行一次HTTP请求或者一次JDBC查询。虚拟线程也支持线程局部变量因为单个JVM可能支持数百万的虚拟线程。然而虚拟线程并不适用于长时间运行的CPU密集型操作。虚拟线程是一种抽象的概念通过Java虚拟机来实现并发执行适用于需要等待I/O操作的任务但并不适用于长时间运行的CPU密集型操作。
虚拟线程的作用 区别于虚拟线程传统的线程对象叫做平台线程platform thread。平台线程在底层 OS 线程上运行 Java 代码并在代码的整个生命周期中占用该 OS 线程因此平台线程的数量受限于 OS 线程的数量。虚拟线程是 java.lang.Thread 的一个实例它在底层 OS 线程上运行 Java 代码但不会在代码的整个生命周期中占用该 OS 线程。也就是说多个虚拟线程可以在同一个 OS 线程上运行其 Java 代码可以有效地共享该线程。平台线程独占宝贵的 OS 线程而虚拟线程则不会因此虚拟线程的数量可以比 OS 线程的数量多得多执行阻塞任务的整体吞吐量也就大了很多。 但如果上述任务不是简单的sleep 1s而是计算了1s例如做矩阵计算或数组排序等用线程池和虚拟线程的执行时间区别就没有那么大。原因是虚拟线程虽然可以带来更大的吞吐量但并不能让单个任务计算得更快当使用平台线程执行任务已经让cpu没有任何空闲时切换虚拟线程来执行也不会带来任何收益。 虚拟线程可以发挥的最大作用是可以让采用单请求单线程thread-per-request的方式编写的服务器程序最大化地利用CPU计算资源 。 其原因在于服务器程序有两大特点一是需要处理较大吞吐量的请求二是请求处理的过程大多是由IO密集型逻辑组成这就导致采用平台线程实现的单请求单线程编写方式可能会有大量的IO阻塞占据了平台线程资源从而不能充分利用CPU资源。我们在使用真实应用压测时观察到当服务请求IO耗时增大时使用虚拟线程的吞吐量会明显高于线程池尤其是当服务下游依赖出现故障导致耗时增大时虚拟线程带来的服务可用性提升会非常明显。 有些情况下服务端开发者为了充分利用cpu硬件资源会考虑放弃单请求单线程的编程风格而采用基于netty、actor等异步框架来构建服务。这样虽然它消除了由于OS线程稀缺性带来的吞吐量限制但代价很高它需要异步编程风格没有专用线程开发人员必须将请求处理逻辑分解成小阶段通常是lambda表达式或独立的回调handler对象然后使用API将它们组合成一个顺序管道例如CompletableFuture或响应式框架在一定程度上放弃了代码的顺序执行逻辑和代码的可读性。 在异步风格中请求的每个阶段可能在不同的线程上执行每个线程以交替方式运行属于不同请求的阶段。这对于理解程序行为有很大的影响堆栈跟踪不能提供可用的上下文debug无法跟踪请求处理逻辑而分析工具无法将请求处理与其调用者相关联。总的来说除了底层服务框架和一些特定的功能性服务大部分以业务开发为主导的服务器程序都不会采用这种编程风格进行逻辑开发
虚拟线程的工作原理 线程需要被调度才能执行任务本质上是分配到CPU上执行。对于由操作系统线程实现的平台线程JDK 依赖于操作系统中的调度程序而对于虚拟线程JDK 先将虚拟线程分配给平台线程然后平台线程按照通常的方式由操作系统进行调度。 调度器分配给虚拟线程的平台线程称为虚拟线程的载体线程carrier。虚拟线程可以在其生命周期内会被安排在不同的载体线程上。换句话说调度器不维护虚拟线程和平台线程之间的亲和关系。从 Java 代码的角度来看运行中的虚拟线程在逻辑上独立于其当前载体线程 载体线程的信息对虚拟线程不可见Thread.currentThread() 返回的值始终是虚拟线程本身。 载体线程和虚拟线程的堆栈跟踪是分开的。在虚拟线程中抛出的异常将不包括载体线程的堆栈帧。线程dump不会在虚拟线程的堆栈中显示载体线程的堆栈帧反之亦然。 载体线程的thread-local变量对虚拟线程不可用反之亦然。 从 Java 代码的角度来看开发者不能感知到虚拟线程和其载体线程临时共享了一个操作系统线程。但从本地代码native code的角度来看虚拟线程和其载体在同一个本地线程上运行。因此在同一虚拟线程上多次调用的本地代码可能会观察到不同的操作系统线程标识符。
注意事项
JDK中绝大多数的阻塞操作如LockSupport、网络库API、大部分IO操作都会卸载虚拟线程释放其载体线程和底层操作系统线程以执行其他虚拟线程。然而JDK中有一些阻塞操作不会卸载虚拟线程从而阻塞其载体线程和底层操作系统线程这是因为操作系统层面例如许多文件系统操作或JDK层面例如Object.wait()的限制。为了解决这些阻塞操作的问题虚拟线程调度器会通过暂时扩展并行度来弥补平台线程被占用因此在调度程序的ForkJoinPool中平台线程的数量可能暂时超过可用处理器的数量可以通过系统属性jdk.virtualThreadScheduler.maxPoolSize来调整调度器可用的平台线程的最大数量。
但有两种情况下虚拟线程在阻塞操作期间不能卸载而是被固定pinned在其载体线程上 当虚拟线程在 synchronized 代码块或方法中执行代码时 当它执行本地方法native method 或外部函数foreign function时