线程阻塞状态
线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
线程运行过程中,可能由于各种原因进入阻塞状态:
- 线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
- 线程试图得到一个锁,而该锁正被其他线程持有;
- 线程通过调用sleep方法进入睡眠状态;
- 线程在等待某个触发条件;
- ......
以上原因可以划分成三大类:线程阻塞、线程睡眠、线程挂起。
线程阻塞、线程睡眠、线程挂起的形象比喻
操作系统中阻塞、睡眠、挂起的区别形象解释。首先这些术语都是对于线程来说的,对线程的控制就好比你控制了一个雇工为你干活。
线程阻塞:本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。雇工一旦发现扫帚回来了,他就会自己去干活的。
线程睡眠:你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
挂起线程:你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
线程阻塞
线程在运行过程中必然要获取访问系统资源,暂且不说CPU,线程运行肯定要和磁盘进行交互,继而发生IO操作,IO操作势必要引起等待,在资源未读取完成,线程必然要等待,那么在等待IO完成这个部分就是阻塞状态。所以从这里来看,阻塞是一种被动的方式,由于获取资源获取不到而引起的等待。
本人将线程阻塞细分成两类:
- 等待阻塞:等待IO等硬件资源
- 同步阻塞:等待信号量等内核资源,延伸的还有互斥锁、条件变量等线程同步对象(线程Join操作原理估计也是同步阻塞)。
线程睡眠
当一个线程获取资源失败(但没有触发线程阻塞)暂时无法继续运行时,可以有两种处理方式:
- 放弃CPU,自己睡眠,触发调度;
- 占有CPU,忙等待,使用完自己的时间。
从这里看,睡眠是一种主动的方式,且仅仅作为一种处理手段。睡眠不仅仅用于使线程进入阻塞状态,更多的,我们可以在适当的时候设置让线程睡眠一定的时间。
线程挂起
线程挂起是操作系统调度线程的手段之一,操作系统在调度时暂停当前线程的执行,将其切换至阻塞状态,将CPU资源调度给其他的线程;在需要的时候,操作系统可以恢复某线程的执行权限,将其切换至就绪状态。
因此常见的线程挂起是一种被动的方式,场景如下:
- 分时操作系统当前进程时间片用完时,被动切换至阻塞状态
- 抢占式操作系统当前进程被高优先级进程抢占后,被动切换至阻塞状态
某些情况下线程挂起是主动触发的,比如调试程序时,线程执行到某个断点,会主动挂起线程(trap陷入)。
进程挂起
线程挂起主要考虑CPU资源的调度,而进程挂起主要考虑是内存资源的分配。
挂起进程在操作系统中可以定义为暂时被淘汰出内存的进程,机器的资源是有限的,在资源不足的情况下,操作系统对在内存中的程序进行合理的安排,其中有的进程被暂时调离出内存,当条件允许的时候,会被操作系统再次调回内存,重新进入等待被执行的状态即就绪态。
操作系统为什么要引入挂起状态?挂起状态涉及到中级调度,因为当内存中的某个程序需要大的内存空间来执行,但这时内存有没有空余空间了,那么操作系统就回根据调度算法把一些进程放到外存中去,以腾出空间给正在执行的程序的数据和程序,所以引如了挂起状态。引起挂起状态的原因有如下几方面:
(1)终端用户的请求。当终端用户在自己的程序运行期间发现有可疑问题时,希望暂停使自己的程序静止下来。亦即,使正在执行的进程暂停执行;若此时用户进程正处于就绪状态而未执行,则该进程暂不接受调度,以便用户研究其执行情况或对程序进行修改。我们把这种静止状态成为“挂起状态”。
(2)父进程的请求。有时父进程希望挂起自己的某个子进程,以便考察和修改子进程,或者协调各子进程间的活动。
(3)负荷调节的需要。当实时系统中的工作负荷较重,已可能影响到对实时任务的控制时,可由系统把一些不重要的进程挂起,以保证系统能正常运行。
(4)操作系统的需要。操作系统有时希望挂起某些进程,以便检查运行中的资源使用情况或进行记账。
(5)对换的需要。为了缓和内存紧张的情况,将内存中处于阻塞状态的进程换至外存上。