Java线程的底层实现

主流的操作系统都提供了线程的实现,注意这句话,谁实现的线程?是操作系统,实际上实现线程的老大哥,是运行在内核态的操作系统。

 

Java 语言提供了不同硬件和操作系统平台下对线程操作的统一处理,每个已经执行 start() 且还未结束的 java.lang.Thread 类的实例就代表了一个线程。但是正如我们刚刚所强调的那样,线程可是由操作系统来实现的啊,那么 Java 是如何面向开发者提供的线程统一操作呢?我们来简单的看一下 Thread 类的几个关键方法。
 

    private static native void registerNatives();
    
    public static native Thread currentThread();

    public static native void yield();
    
    public static native void sleep(long millis) throws InterruptedException;

    public final native boolean isAlive(); 

操作系统实现线程主要有 3 种方式

  • 用户级线程
  • 内核级线程
  • 用户级线程 + 内核级线程,混合实现

内核线程

内核线程就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身。

程序一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(Light Weight Process,LWP),轻量级进程就是我们通常意义上所讲的线程,由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。这种轻量级进程与内核线程之间 1 : 1 的关系称为一对一的线程模型,如下图所示。

由于有内核线程的支持,每个轻量级进程都成为一个独立的调度单元,即使有一个轻量级进程在系统调用中阻塞了,也不会影响整个进程继续工作,但是轻量级进程具有它局限性,主要有如下两点

线程的创建、销毁等操作,都需要进行系统调用,而系统调用的代价相对较高,需要在用户态和内核态之间来回切换。
每个轻量级进程都需要有一个内核线程的支持,因此轻量级进程要消耗一定的内核资源(比如内核线程的栈空间),因此一个系统支持轻量级线程的数量是有限的。
 

用户线程

从广义上讲,一个线程只要不是内核线程,就可以认为是用户线程。从狭义上讲,用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。如果程序实现得当,这种线程不需要切换到内核态,因此操作可以是非常快速且低消耗的,并且可以支持规模更大的线程数量。这种进程与用户线程之间 1 : N 的关系称为一对多的线程模型,如下图所示。

使用用户线程的优势在于不需要内核支援,劣势也在于没有内核的支援,所有的线程操作都需要用户程序自己处理。线程的创建、切换和调度都是需要考虑的问题。因而使用用户线程实现的程序都比较复杂,除了以前在不支持多线程的操作系统中的多线程程序与少数有特殊需求的程序外,现在使用用户线程的程序越来越少了。