线程
线程是比进程更轻量级的调度执行单位,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、文件I/O等),又可以独立调度。
实现线程的方式
内核线程
- 就是直接由操作系统内核支持的线程,由内核来完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映射到各个处理器上。
- 程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程。
- 轻量级进程局限性:基于内核线程实现的,创建、析构及同步操作,都需要进行系统调度;每个轻量级进程都需要一个内核线程的支持,要消耗一定的内核资源(如内核线程的栈空间)。
内核线程
用户线程
- 完全建立在用户空间的线程库上,用户线程的建立、同步、销毁和调度完全在用户态中完成。
- 用户线程的优势:不需要系统内核支援
- 用户线程的劣势:因为没有系统内核的支援,所有线程操作都需要由用户程序自己处理。
用户线程
混合线程
- 内核线程与用户线程一起使用。既存在用户线程,也存在轻量级进程。
- 用户线程完全建立在用户空间中,用户线程的创建、切换、析构等操作消耗的资源少,并可以支持大规模的用户线程并发。
- 轻量级进程则作为用户线程与核心线程之间的桥梁,可以提供线程调度及处理器映射,并用户线程的系统调用通过轻量级进程完成,降低了整个进度被阻塞的风险。
混合线程
Java线程调度
- 协同式:线程的执行时间由线程本身控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上去。
- 协同式多线程的最大好处是实现简单,而且由于线程要把自己的事情干完后才会线程切换,切换操作对线程自己是可知的,不会出现线程同步的问题。
- 协同式线程的坏处,线程执行时间不可控制,如果一个线程的代码编写有问题,一直不通知系统进行线程切换,那么程序会一直阻塞。
- 抢占式:每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。例如Thread::yield()方法可以主动让执行时间是系统可控的。不会有一个线程导致整个进行甚至系统阻塞。
线程状态
- 新建(New):创建后尚未启动的线程
- 运行(Runnable):包括操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着操作系统为它分配执行时间。
- 无限期等待(Waiting):不会被分配处理器执行时间,它们要等待被其他线程显式唤醒。
- 没有设置Timeout参数的Object::wait()方法
- 没有设置Timeout参数的Thread::join()方法
- LockSupport::park()方法
- 限期等待(Timed Waiting):不会被分配处理器执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后他们会由系统自动唤醒。
- Thread::sleep()方法
- 设置了Timeout参数的Object::wait()方法
- 设置了Timeout参数的Thread::join()方法
- LockSupport::parkNanos()方法
- LockSupport::parkUntil()方法
- 阻塞(Blocked):线程被阻塞了,"阻塞状态"与"等待状态"的区别是"阻塞状态"在等待着获取到一个排它锁,这个事件将在另外一个下次呢很那个放弃这个锁的时候发生;而"等待状态"则是在等待一段时间,或者唤醒动作的发生。
- 结束(Terminated):已终止线程的线程状态,线程已经结束执行。
线程状态转换图