目录
一、线程状态与生命周期
每个Java程序都有一个默认的主线程,对于应用程序来说其主线程是main()方法执行的线程;对小程序来说,其主线程指挥浏览器加载并执行Java小程序。实现多线程须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,一个线程在它的一个完整的生命周期内通常要经历下面五种状态:
1. Java线程的五种状态
(1)新建状态(newborn):当一个Thread类或其子类的对象被声明并创建但还未被执行时处于新建状态中。此时,线程对象已经被分配了内存空间和其他资源,并已被初始化,但是尚未被调度。
(2)就绪状态(runnable):处于新建状态的线程被启动后,将进入线程队列排队等待CPU资源,此时它已具备了运行的条件。一旦轮到它来占用CPU资源时,就会脱离创建它的主线程独立开始自己的生命周期。处于阻塞状态的线程被解除阻塞后也将进入就绪状态。
(3)执行状态(running):当就绪状态的线程被调度并获得CPU资源时,便进入执行状态。当线程对象被调度执行时,它将自动调用本对象的 run() 方法执行直至结束,除非该线程主动让出CPU的控制权。
处于执行状态的线程在下列情况下将让出CPU的控制权
- 线程执行完毕
- 有比当前线程优先级更高的线程处于就绪状态
- 线程主动睡眠一段时间
- 线程在等待某一资源
(4)阻塞状态(blocked):一个正在执行的线程如果在某些特殊情况下,将让出CPU并暂时中止自己的执行,线程处于这种不可执行的状态被称为阻塞状态。阻塞状态是因为某种原因系统不能执行线程的状态,在这种状态下即使CPU空闲也不能执行线程。
下面几种情况可使一个线程进入阻塞状态:
- 调用sleep() 方法;
- 为等待一个条件变量,线程调用wait() 方法;
- 该线程与另一线程join() 在一起。
一个线程被阻塞时它不能进入排队队列,只有当引起阻塞的原因被消除时,线程才可以转入就绪状态,重新进到线程队列中排队等待CPU资源,以便从原来的暂停处继续执行。处于阻塞状态的线程通常需要由某些事件才能唤醒:处于睡眠状态的线程必须被阻塞一段固定的时间,当睡眠时间结束时就变成就绪状态;因等待资源或信息而被阻塞的线程则需要由一个外来事件唤醒。
(5)终止状态(dead):处于终止状态的线程不具有继续执行的能力。导致线程消亡的原因有两个:一是正常运行结束,即执行完run()方法的最后一条语句并退出;二是当进程因故停止运行时,该进程中的所有线程将被强行终止。当线程处于终止状态、并且没有该线程对象的引用时,Java的垃圾回收器会从内存中删除该线程对象。
2. 线程的生命周期
二、线程的优先级与调度
1)优先级
在多线程系统中,每个线程都被赋予一个执行优先级。优先级决定了线程被CPU执行的优先顺序。
Java语言中线程的优先级从低到高以整数1~10表示,共分为10级。Thread类有三个关于线程优先级的静态常量:MIN_PRIORITY表示最小优先级,通常为1;MAX_PRIORITY表示最高优先级,通常为10;NORM_PRIORITY表示普通优先级,默认值为5。
对应一个新建的线程,系统会遵循如下的原则为其指定优先级。
(1)新建线程将继承创建它的父线程的优先级。父线程是指执行创建新线程对象语句所在的线程,它可能是程序的主线程,也可能是某一个用户自定义的线程。
(2)一般情况下,主线程具有普通优先级。另外,如果想要改变线程的优先级,可以通过调用线程对象的setPriority()方法来进行设置。
2)调度:
在各个线程之间分配CPU资源,多个线程的并发执行实际上就是调度来进行的。
线程调度的两种模型:分时模型和抢占模型。
① 分时模型:CPU资源按照时间片分配,获得CPU资源的线程只能在指定的时间片内执行,一旦时间片使用完毕,就必须把CPU资源让给另一个处于就绪状态的线程。
② 抢占模型:当前活动的线程一旦获得执行权,将一直执行下去,直到执行完或由于某种原因主动放弃执行权。如在一个低优先级线程的执行过程中,又有一个高优先级的线程准备就绪,那么低优先级的线程就把CPU资源让给高优先级的线程。为了使低优先级的线程有机会执行,高优先级的线程应该不时地主动进入“睡眠”状态,而暂时让出CPU。
3)java的调度方法:
- 同优先级的线程组成先进先出队列(先来先服务),分时调度
- 高优先级使用优先调度的抢占调度
三、多线程的并发与并行
- 并发:单核cpu运行多线程时,时间片进行很快的切换,线程轮流执行cpu
- 并行:多核cpu运行多线程时,真正的在同一时刻运行(实际中多为并行执行)