每个已经执行 start() 且未结束的 java.lang.Thread 类的实例都代表一个线程。
Thread 类与大部分 java API 的显著区别在于,所有关键方法都声明为 Native,意味着 Thread 类的关键方法都需要使用平台相关的手段实现。
实现线程的3种方式:使用内核线程实现、使用用户线程实现、使用用户线程加轻量级进程混合实现
1、使用内核线程实现:
直接由操作系统内核支持的线程,内核通过操纵调度器对其进行调度,并将任务映射到各个处理器。支持多线程的内核叫做多线程内核。
程序一般不直接使用内核线程,而是使用它的高级接口——轻量级进程(也就是通常说的线程)。由于每个轻量级进程都有一个内核线程支持,因此要先支持内核线程才能支持轻量级进程。轻量级进程与内核线程间关系为1:1。
优点:因为每个轻量级进程都有独立的内核线程提供支持,所以都是独立调度单元,互相之间不影响。
缺点:因为基于内核线程实现,所以要消耗内核资源,导致数量有限。并且线程的所有操作都要进行系统调用。而系统调用需要在用户态和内核态之间来回切换,代价较高。
2、使用用户线程实现:
此处的用户线程指完全建立在用户空间中的线程库上的线程。系统内核无法感知,操作都在用户态中完成,无需内核帮助(进程与用户线程间关系为 1:N)。
优点:操作可以非常快速且消耗低,可以支持更大规模的线程数量(部分高性能数据库的多线程由此方式实现)。
缺点:没有系统内核帮助,线程操作需要完全由用户程序自身来处理。而且由于操作系统只将处理器资源分配到进程,所以解决涉及处理器的问题会非常困难。也因此用户线程实现的程序一般比较复杂。(使用用户线程的程序逐渐减少)
3、使用用户线程加轻量级进程混合实现:
此方式将前两者结合,用户线程依然完全建立在用户空间中,依然享受原有的优点;操作系统支持的轻量级进程则作为其与内核线程间的桥梁,提供系统内核线程以帮助处理处理器映射、线程调度等问题,大大降低进程被完全阻塞的风险。此模式中,两者比例并不固定(N :M)。
4、Java线程实现
JDK 使用的线程模型依据操作系统的不同而改变。即操作系统的线程模型就是 JDK 的线程模型。为此,JDK 在 Windows 及 Linux 上使用的都是一对一的线程模型。
Java线程调度
线程调度指系统为线程分配处理器使用权的过程,共有两种方式:协同式与抢占式
协同式:
线程执行时间由线程自身进行控制,线程执行完工作后要主动告知系统进行线程切换。
优点:实现简单,没有线程同步问题
缺点:线程执行时间不可控,一个线程有问题,会导致持久的阻塞,甚至是系统崩溃
抢占式:
线程由系统分配执行时间,线程切换并非由线程自身决定(Thread.yield()可以让出时间,但 java 中没有方法让线程获取执行时间)。该方式为 java 选用的实现方式。
java 虽然提供了10个级别的优先级概念,但由于线程是要映射到系统原生线程上进行实现,所以调度还是取决于操作系统。而且在 windows 中还存在“优先级推进器”(系统发现一个线程经常执行的话,有可能越过优先级去为其分配执行时间)。因此,优先级未必有效。
线程状态
状态名称 | 具体含义 |
---|---|
新建(New) | 创建后未启动 |
运行(Runnable) | 此状态下的线程,可能正在执行,也可能在等待 CPU 分配时间 |
无限期等待(Waiting) | 此状态下的线程需要等待被其它线程唤醒(Object.wait()、Thread.join()、LockSupport.park()) |
限期等待(Timed Waiting) | 线程在一定时间后由系统唤醒(Thread.sleep()、LockSupport.parkNanos()、LockSupport.parkUntil()、设置了 Timeout 参数的 Object.wait() 与 Thread.join() ) |
阻塞(Blocked) | 线程等待获取排它锁,该状态在线程进入同步区域时发生 |
结束(Terminated) | 线程结束执行 |