Java线程模型
本文将从线程类型、线程通信、线程调度三个方面分析Java中的线程模型。
什么是线程?
线程就是进程的切片。因为CPU速度太快,进程切换时cpu都要进行进程上下文的加载、保存操作。对CPU来说是巨大的性能浪费。所以引入线程的目的就是为了细化进程对CPU时间占用粒度,更加充分的利用CPU。
因为线程是对进程的切片,所以线程是共享进程资源的,同一进程里线程的切换不会影响进程的切换。
与进程的区别
- 进程是资源分配的最小单位,线程是调度的最小单位。
- 进程独占资源,线程共享进程资源。
线程的类型
内核线程(KLT)
内核线程就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核。
轻量级进程(LWP)
轻量级进程是由系统提供给用户的操作内核线程的接口的实现。即轻量级进程是内核线程的一个替身。
用户线程(UT)
用户线程建立在用户空间的线程库上,系统内核不能感知线程存在的实现。用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。如果程序实现得当,这种线程不需要切换到内核态,因此操作可以是非常快速且低消耗的,也可以支持规模更大的线程数量,部分高性能数据库中的多线程就是由用户线程实现的。
线程模型
一对一模型
一对一模型即轻量级进程的实现模式。一个LWP对应一个KLT。
优点:每个LWP都是独立的调度单元,一个线程阻塞不影响其他线程。
缺点:因为与KLT一对一。而KLT创建,调度需要一定内核资源,因此创建数量有限。
一对多模型
一对多模型及用户线程实现方式。一个进程对应多个UT。
优点:线程的管理在用户空间进行。比较高效。
缺点:需要用户自己考虑线程的调度相关问题,因为系统对UT无感知,所以一个线程阻塞会导致进程阻塞。
多对多模型
在一对多的基础上引入KLT。即一个进程管理多个KLT,KLT对应多个UT。
将 n 个UT映射到m个KLT上,要求 m <= n。
优点: 集合了前两种模型的优点,去掉了他们的缺点。
缺点:全是优点。
线程的调度方式
抢占式调度
每个线程由系统分配CPU时间,线程本身无法控制使用多次CPU时间。好处是线程的执行时间是可控的,不会造成因为一个线程导致进程长时间阻塞问题。
协同式调度
线程自己控制CPU时间。并且当前线程执行完毕后需要通知系统切换另外一个线程。最主要的问题是线程的切换取决于线程本身,若线程存在Bug导致切换线程不成功则会一直阻塞。
线程间通信与同步
线程虽然可以独立的执行。但是总会有需要不同的线程互相配合的情况,这就涉及到线程的通信与同步。目前有两种方式。
共享内存
共享内存的并发模型里线程通过显示的同步即通过互斥实现对公共空间的读写,将需要共享的数据同步到公共的内存中,这样便实现了间接的通信。即共享内存是显示同步,隐式通信。
消息传递
消息传递模型即消息传递,线程间无公共内存,因为消息通信天然具有先后关系所以间接实现类数据的同步。所以消息传递模型是显示通信,隐式同步。
Java中的线程
线程模型
目前Java默认使用的是一对一模型。即LWP方案。不过在支持多对多的平台上可以通过JVM参数控制使用多对多模型。
相关参数
-XX:+UseLWPSynchronization(默认值)
-XX:+UseBoundThreads
调度方式
Java采用抢占式调度。同时Java定义了10中线程优先级。可以在一定程度上调整线程的CPU时间。
通信机制
Java采用的是共享内存并发模型。具体可参考Java内存模型。
参考
- <<深入理解Java虚拟机: JVM高级特性与最佳实践>>
- <<Java并发编程的艺术>>