主流的操作系统实现线程的方式主要有三种:内核线程,用户线程,用户线程加轻量级进程混合实现。
了解jvm是如何实现java线程之前先对操作系统的三种实现方式有个基础的认识。
关于这三种的介绍强烈推荐大家去看《深入理解Java虚拟机 第3版》的第12章的 Java与线程 部分,讲的非常好,我在这里就根据此章节做个总结

内核线程

以下总结基于书本原话做了一些删减:
使用内核线程实现的方式也被称为1:1实现,内核线程(KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。
程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(LWP),轻量级进程就是我们通常意义上所讲的线程(JVM就是通过这种方式实现线程),由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。
轻量级进程有一定的局限性:
首先,由于是基于内核线程实现的,所以各种线程的操作,如创建,析构及同步都需要进行系统调用,而系统调用的代价是相对较高的,需要在用户态和内核态中来回切换
其次,每个轻量级进程都需要一个内核线程的支持,因此轻量级进程需要消耗一定的内核资源(如内核线程的栈空间),因此一个系统支持轻量级进程的数量是有限的。

优势:线程的操作都交给了操作系统管理,调用系统提供的api即可,系统对线程的创建销毁可感知
劣势:线程的操作需要在用户态和内核态来回切换,消耗资源

用户线程

用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知到用户线程的存在及如何实现。用户线程的创建,同步,销毁和调度完全在用户态中完成,不需要内核的帮助
优势:不需要用户态和内核态的切换,低消耗
劣势:没有系统内核支撑,线程的创建,销毁等复杂的操作都需要用户自己实现

混合实现

即存在用户线程,也存在轻量级进程。

Java代码论证

主流商用Java虚拟机的线程模型普遍都被替换为基于操作系统原生线程模型来实现,即采用1:1的线程模型(书上原话)。采用内核线程的话系统是可感知线程的创建和销毁的,那么即可通过代码来论证
本机当前线程数,可通过任务管理器->性能 查看
image.png
当前线程是2167,线程一直在变化,只是一个大概的数字,接下来执行下面代码,启动1000个线程

  1. public static void main(String[] args) {
  2. for (int i = 0; i < 1000; i++) {
  3. new Thread(new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep(5000);
  8. System.out.println(Thread.currentThread().getName());
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. }).start();
  14. }
  15. }

线程瞬间到达了3200多,代码执行完后,又恢复到了2000多
image.png
由此可见,线程的创建和销毁对系统来说都是可感知的,那么便可知Java线程的实现是基于内核线程的。

Java线程调度

线程调度是指系统为线程分配处理器使用权的过程,调度分两种:协同式调度和抢占式调度

协同式:

特点:线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上去。
缺点:线程执行时间不可控,如果一个线程有问题,一直不告知系统进行线程切换,那么程序会一直阻塞
优点:实现简单。

抢占式:(Java的实现方式是这种)

特点:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。
优点:解决了协同式的缺点,不会阻塞程序

线程状态转换timg.jpg