一、线程的状态
新建状态、就绪状态、阻塞状态、死亡状态。
1、新建状态
新创建一个线程对象。
2、就绪状态
线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行的线程池”中,变得可运行,只等待获取CPU的使用权。即在就绪状态的进程除了CPU之外,其他的运行所需资源都已全部获得。
2.1、就绪状态只是说有资格运行,调度程序没有挑选到你,你就永远是可运行状态。
2.2、调用线程的start()方法,此线程进入就绪状态。
2.3、当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
2.4、当前线程时间片用完了,调用当前线程的yeild()方法,当前线程进入就绪方法。
2.5、锁池里的线程拿到对象锁后,进入就绪状态。
3、运行状态
就绪状态的线程获取了CPU,执行程序代码。
3.1、run()方法或main()方法结束后,线程就进入终止状态。
3.2、当线程调用了自身的sleep()方法或其他线程的join()方法,进程让出CPU,然后就会进入阻塞状态(该状态即停止当前线程,但并不释放所占有的资源即调用sleep()方法后,线程不会释放它的”锁标志“)。当sleep()结束或join()结束后,该线程进入就绪状态,继续等待OS分配CPU时间片。典型的,sleep()被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
3.3、线程调用了yield方法,意思是放弃当前获得的CPU时间片,回到就绪状态,这是与其他进程处于同等竞争状态,OS有可能会接着让这个进程进入运行状态,调用yield()的效果等价于调度程序认为该线程已执行了足够的时间片从而需要转到另一个线程。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
3.4、当线程进入就绪状态,发现将要调用的资源被synchronized,获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这是的锁池里也许已经有了其他线程在等待获取所标记,这时它们处于队列状态,即先到先得),一旦线程获得锁标记后,就转入就绪状态,等待OS分配时间片。
3.5、suspend()和resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须被对应的resume()调用,才能使得线程重新进入可执行状态。典型的,suspend()和resume()被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生看了结果后,调用resume()使其恢复。
3.6、wait()和notify()方法:当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒所有线程),线程被唤醒后会进入锁池,den
4、阻塞状态
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
4.1、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入”等待池“中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或者notifyAll()方法才能被唤醒。
4.2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入”锁池“中。
4.3、其他阻塞:运行的线程执行了sleep()或join()方法,或者发出了i/o请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者i/o处理完毕时,线程重新转入就绪状态。
4.4、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入就绪状态,等待OS分配CPU时间片;
4.5. suspend() 和 resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型地,suspend()和 resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume()使其恢复。
4.6、wait()和 notify() 方法:当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。
5、死亡状态
线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
二、进程的状态
进程是系统进行资源分配和调度的基本单位。线程是CPU调度的基本单位。
一般来说,进程有三个状态:
1、运行态
进程占用CPU,并在CPU上运行。
2、就绪态
进程已经具备运行条件,但是CPU还没有分配过来。
3、阻塞态
进程因等待某件事发生而暂时不能运行。
上述的三种状态之间转换分为六种情况:
1、运行——>就绪:主要是进程占用CPU的时间过长,而系统分配给该进程占用CPU的时间是有限的;在采用抢先式优先级调度算法的系统中,当有更高优先级的进程要运行时,该进程就被迫让出CPU,该进程便由执行状态转变为就绪状态。
2、就绪——>运行:运行的进程的时间片用完,调度就转到就绪队列中选择合适的进程分配CPU
3、运行——>阻塞:正在执行的进程因发生某等待事件而无法执行,则进程由执行状态变为阻塞状态,如发生了I/O请求
4、阻塞——>就绪:进程所等待的事件已经发生,就进入就绪队列
以下两种状态是不可能发生的:
1、阻塞——>运行:即使给阻塞进程分配CPU,也无法执行,操作系统在进行调度时不会从阻塞队列进行挑选,而是从就绪队列中选取
2、就绪——>阻塞:就绪态根本就没有执行,谈不上进入阻塞态。
在一些系统中,又增加了一些新状态,如挂起状态,可运行状态,深度睡眠状态,浅度睡眠状态,暂停状态,僵死状态。
1、可运行状态:运行状态和就绪状态的合并,表示进程正在运行或准备运行,Linux 中使用TASK_RUNNING 宏表示可运行状态。
2、浅度睡眠状态:进程正在睡眠(被阻塞),等待资源的到来是唤醒,也可以通过其他进程信号或时钟中断唤醒,进入运行队列。Linux 中使用TASK_INTERRUPTIBLE 宏表示此状态。
3、深度睡眠状态:其和浅度睡眠基本类似,但不可被其他进程信号或时钟中断唤醒。Linux 中使用TASK_UNINTERRUPTIBLE 宏表示此状态。
4、暂停状态:进程暂停执行接受某种处理。Linux 使用TASK_STOPPED 宏表示此状态。
5、僵死状态:进程已经结束但未释放进程控制块(PCB),Linux 使用TASK_ZOMBIE 宏表示此状态。
挂起状态:在执行状态的进程通过挂起即可进入就绪状态,如图所示,就绪状态和阻塞状态都分为活动态和静止态。由活动态向静止态转换就是通过挂起实现的。
引入挂起状态的原因有:
1、终端用户的请求。当终端用户在自己的程序运行期间发现有可疑问题时,希望暂时使自己的程序静止下来。亦即,使正在执行的进程暂停执行;若此时用户进程正处于就绪状态而未执行,则该进程暂不接受调度,以便用户研究其执行情况或对程序进行修改。我们把这种静止状态称为挂起状态。
2、父进程请求。有时父进程希望挂起自己的某个子进程,以便考查和修改该子进程,或者协调各子进程间的活动。
3、负荷调节的需要。当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行。
4、操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。