线程的实现
1.使用内核线程实现
内核线程(KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。程序一般不会直接使用内核线程,而是去使用内核线程的一种高级接口——轻量级进程(LWP),轻量级进程就是我们通常意义上的线程,由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核,才能有轻量级进程。这种轻量级进程与内核线程之间1:1的关系称为一对一的线程模型:
2.使用用户线程实现
狭义上的用户线程指的是完全建立在用户空间的线程库上,系统内核不能感知线程存在的实现。用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。因为不需要切换到内核态,操作可以时非常快速且低消耗的,也可以支持规模较大的线程数量。这种进程与用户线程之间1:N的关系称为一对多的线程模型:
由于缺乏系统内核的支援,线程实现起来异常困难,现在使用用户线程的程序越来越少了,Java、Ruby等语言都已经放弃使用用户线程。
3.使用用户线程加轻量级进程混合实现
这种混合模式中,用户线程与轻量级进程的数量比是不定的,即为N:M的关系:
许多UNIX系统的操作系统都提供了N:M的线程模型实现。
4.Java线程的实现
对于Sun JDK,它的Windows版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程之中,因为Windows和Linux系统提供的线程模型就是一对一的。而在Solaris平台中,由于操作系统的线程特性可以同时支持一对一和多对多的线程模型,因此Solaris版的JDK重对应提供了两个平台专有的虚拟机参数用于指定虚拟机使用那种线程模型。
Java线程调度
线程调度方式主要有协同式和抢占式两种。
Java线程调度是系统自动完成的,但是我们可以通过设置线程优先级来“建议”系统给某些线程多分配一点执行时间。不过线程优先级并不完全靠谱,因为线程调度取决于操作系统,而不同操作系统对优先级支持不同。
状态转换
Java语言定义了五种线程状态,在任意一个时间点,一个线程只能有且只有其中一个状态:
- 新建(New):创建后尚未启动的线程处于这种状态
- 运行(Runable):包括了操作系统线程状态中的Running和Ready,也就是处于该状态的线程有可能正在执行,也可能正在等待CPU为它分配执行时间
- 无期限等待(Waiting):该状态的线程不会被分配CPU执行时间,要等待被其他线程显示地唤醒,在一定时间后会由系统自动唤醒。一下方法会让线程进入无限期等待:
Thread.sleep()方法
设置了Timeout参数的Object.wait()方法
设置了Timeout参数的Thread.join()方法
LockSupport.parkNanos()方法
LockSupport.parkUntil()方法 - 阻塞(Blocked):阻塞状态在等待获取一个排他锁,这个事件将在另一个线程放弃这个锁的时候发生
- 结束:已终止线程的线程状态
下图是5种状态之间的转换关系: