操作系统线程与Java线程的区别
用户态线程
第一阶段:
操作系统在最开始的时候没有线程的概念,刚开始只有进程,操作系统分配资源的最小单位是进程,进程与进程之间是隔离的,每个进程都有自己的内存空间。CPU
调度以进程作为最小调度单元。
第二阶段:
初期的多线程,线程是在用户空间下实现的。
我们都知道内存分用户空间和系统空间,系统空间是给操作系统使用的,用户空间是应用程序使用的,应用程序如果需要访问系统空间,需要进行系统调用,从用户态切换到内核态。
那怎么在用户空间实现的多线程呢?
实际上是操作系统按进程维度来调度,操作系统是不去管你用户线程的切换的,应用程序自己在用户空间实现线程的创建、维护和调度。模型如下图:
当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。
这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。在JDK1.1
中,就用的绿色线程,而不是原始线程。
这种模式的优点和缺点都非常明显:
缺点: 因为操作系统不知道线程的存在,CPU
的时间片切换是以进程为维度的,如果进程中有某个线程进行了某些耗时长的操作,会阻塞整个进程。另外当一个进程中的某一个线程(绿色线程)进行系统调用时,比如网络IO
、缺页中断等操作而导致线程阻塞,操作系统也会阻塞整个进程,即使这个进程中其它线程还在工作。
优点: 使用库函数来实现的线程切换,就免去了用户态到内核态的切换,Go
的协程就有借鉴了一部分这个思想。
内核态线程
内核线程就是直接由操作系统内核(Kernel
)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler
)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel
)。通俗的讲就是,程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对CPU
资源的抢占使用。
Java
线程
Java线程在JDK1.2
之前,是基于称为“绿色线程”(Green Threads
)的用户线程实现的,而在JDK1.2
中,线程模型替换为基于操作系统原生线程模型来实现。因此,在目前的JDK
版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没有办法达成一致,虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对Java程序的编码和运行过程来说,这些差异都是透明的。
也就说JDK1.2
之前,程序员们为JVM开发了自己的一个线程调度内核,到操作系统层面就是用户空间内的线程实现。而到了JDK1.2
及以后,JVM
选择了更加稳健且方便使用的操作系统原生的线程模型,通过系统调用,将程序的线程交给了操作系统内核进行调度。也就是说,现在的Java
中线程的本质,其实就是操作系统中的线程,Linux
下是基于pthread
库实现的轻量级进程,```Windows下是原生的系统``Win32 API
提供系统调用从而实现多线程。
操作系统中线程和Java
线程状态的关系
从实际意义上来讲,操作系统中的线程除去new
和terminated
状态,一个线程真实存在的状态,只有:
ready
:表示线程已经被创建,正在等待系统调度分配CPU使用权。running
:表示线程获得了CPU使用权,正在进行运算waiting
:表示线程等待(或者说挂起),让出CPU资源给其他线程使用对于Java中的线程状态:
无论是Timed Waiting
,Waiting
还是Blocked
,对应的都是操作系统线程的**waiting
(等待**)状态。
而Runnable
状态,则对应了操作系统中的ready
和running
状态。