1. 什么是线程
操作系统中,进程是资源分配的基本单位,这里的资源包括内存空间、文件句柄等,一个进程中,可能包含多个线程,线程相较于进程,更加轻量级,其依托于进程而存在。我们编写的代码,在计算机中运行时是由线程具体执行的。一个进程里面的多个线程都可以使用进程分配到的各项资源:如内存空间、文件句柄等。当多个线程竞争内存中的同一个区域时,就会产生资源竞争问题,这种问题不处理好,程序执行过程中就会带来意向不到的问题。
操作系统其实调度的是各个线程,按照时间片轮转等规则,来获取CPU核心的执行时间,进程本身并不参与这种计算,它只提供资源,我们可以理解线程是程序执行的基本单位。我们可以理解为进程就是一个军营,提供统一的粮草、武器,线程是里面的士兵,有的负责巡逻,有的负责守卫,不同士兵职责不同,但都从军营获得补给,然后各司其职,当两个士兵看上军营里同一个馒头的时候,如果打起来了,那就不妙了。
当我们启动一个Java进程的时候,其实启动的是一个Java虚拟机进程,我们可以在该进程中创建多个线程,完成不同的工作。其实JVM进程本身就是多线程的,比如启动JVM进程后,会启动垃圾回收器线程。
2.Java线程与操作系统线程的关系
JVM进程中的线程和操作系统的线程是一比一映射的,每启动一个Java线程,就要对应创建一个操作系统内核线程,而操作系统创建的内核线程数量是有限的,Linux操作系统为例,默认每个进程约1000个线程,创建太多线程,会消耗完计算机资源。另外线程之间的切换代价较高,线程切换需要内核介入,涉及CPU寄存器的保存和恢复,内核态切换等操作,当线程数远大于CPU内核数量的情况下,频繁的线程切换会导致大量的性能消耗。
NIO模式下,通过事件触发机制和少量线程轮询,大大减少了线程切换导致的性能开销,可以处理成千上万请求,可以提升系统的吞吐量。BIO模式下,一个请求会创建一个线程,会影响系统的吞吐量。
3. JDK21中的虚拟线程
JDK21引入了虚拟线程,虚拟线程是用户态轻量级线程,其底层仍由操作系统线程承载,通过内核线程复用实现了资源优化。而且虚拟线程是由JVM调度的,不用分配内核资源(如内存栈、调度上下文),创建百万级虚拟线程仅需MB级内存,而同等数量平台线程需GB级。虚拟线程切换时,应该是由JVM来进行管理线程上下文的,对上下文进行保存和恢复,不涉及CPU寄存器的保存和恢复以及内核态的切换,代价很低。这样,在有限数量内核线程的情况下,可以创建很多的虚拟线程。附着在内核线程上的虚拟线程遇到IO阻塞时,会直接卸载,由另一个虚拟线程来执行。通过虚拟线程,可以极大的增加系统的吞吐量。这类似于我们有10条生产线(10个核心线程),生成线上的工人(虚拟线程)累了以后,就换一个工人上来,生产线不停工。