05-线程的状态以及各状态之间的转换详解

线程被创建出来之后,它并不是会立刻执行,而是由多个线程进行抢占CPU资源,那么,哪一个线程抢占到了,那一个线程就来执行。就像之前讲的烤烧饼的例子一样,炉子上面有多个任务在转,转到哪个任务(烧饼),那么,那个任务就执行,也就是说,在这个过程中,涉及到线程的生命周期的问题,就是说,从线程的创建到线程的结束的这个过程,它所经历的各个阶段,即线程的状态

05-线程的状态以及各状态之间的转换详解

那么,可以说,线程从创建到死亡,可以说有七个状态,首先第一个我们把它称之为实例化,也就是所谓的初始化状态,那么,初始化状态调用了start()方法,start()方法是启动一个线程,那么就变成了ready to run,ready to run并不是run,而是一个准备运行状态,它也是属于就绪状态,由就绪状态我们发现,如果此时它抢到了CPU资源,就变成了运行状态,就是运行中状态(Running),那么,当它运行完毕之后,正常的就结束了,那么就到了Dead,也就是所谓的终止阶段,死亡阶段,说明这个过程就正常结束了,这就是一个完整的生命周期,但是,在这个过程中,我们知道,就像人的一生一样,总会遇到很多的磨难,比如说,在准备就绪的阶段,突然遇到了stop,于是就死了。当正处于运行状态的时候,突然调用sleep方法,于是就睡觉了,或者当正处于运行状态的时候,突然调用wait方法,于是就到了等待状态,或者当正处于运行状态的时候,遇到了IO的阻塞或进入到一个同步代码块,于是在外面等待。

Blocked就是阻塞状态。

Sleeping和Waiting这两个状态我们可以怎么理解呢?Sleeping其实也是一种等待,它是一种超时等待,这个超时等待就是说,当一定时间过了之后,它就能自己再回到ready to run状态去竞争CPU资源。而Waiting,我们就认为它是一个等待状态,那么,如果没有人唤醒它,它将一直等待下去,必须要有人通过notify()或者notifyAll()来进行唤醒(上图中这里写的不对,)就是说,超时等待这一块,你可以设置超时等待的时间,那么,等它的时间到了之后,它就会自动的去进入到ready to run(就绪状态)去竞争CPU的资源。

阻塞状态就是说,当数据获取到了它就不再阻塞到IO上了,于是就进入了Running(执行状态),或者就是说Lock obtained,就是说拿到这个锁之后,它也就不再进行阻塞了,当然了,在阻塞过程中也有可能会直接进入Dead(终止状态)。

由Waiting(等待状态)或者Sleeping(超时等待状态)是不会直接进入到Dead(终止状态的)。只可能从运行状态进入到等待状态或者超时等待状态,从等待状态和超时等待状态只能回到就绪状态。

这就是整个线程的整个生命周期。

 

下面我们就通过代码来演示一下线程的整个生命周期。

 

05-线程的状态以及各状态之间的转换详解

这就是所谓的初始化状态,也就是被new出来的时候就是一个初始化状态,等我们调用start()方法之后,就变成了ready to run,而并不是Running,那么,怎么来验证它是ready to run而不是Running呢?我们说,如果创建了多个线程,那么就会有多个处于就绪状态,它们同时去争夺CPU资源,那么,谁抢到了那么谁就变成了Running,谁没有抢到,那么它依然就处于ready to run,那么,也就是说,当我们的CPU是单核的时候,那么,如果有多个线程,那么就会多个线程同时抢用这一个CPU资源,那么,也就是说,是有一部分线程处于等待。

所以就是说,当你调用了start()方法之后,它只是处于就绪状态,它就去竞争CPU资源,那么,如果抢到了CPU资源,就变成Running,那么,如果没有抢到,那么,依然处于就绪状态,那么,当抢到了CPU资源,在进行Running的时候,它也有可能会被就绪状态的线程去抢占CPU资源,所以说,我们发现并不是一个线程抢占了CPU资源之后就一只执行,而是过了一会又被其他线程给抢占了,就是这么一个情况。

05-线程的状态以及各状态之间的转换详解

 

当线程走到Thread.sleep(100);这段代码的时候,当前的这个线程就会处于sleeping状态,也就是超时等待状态,那么,当等待时间结束了之后,接着进入到就绪状态,去抢占CPU资源。

发现Thread中没有wait()方法,也就是说wait()并不是Thread类的静态方法,它是对象的方法,我们知道,所有类的父类是Object类。等待就是调用对象的wait()方法。调用了wait()方法之后,它会一直处于等待状态,那么,直到调用了notify或者notifyAll之后,它才会处于就绪状态。

我们就用代码简单的测试一下。

我们需要两个线程。

05-线程的状态以及各状态之间的转换详解

05-线程的状态以及各状态之间的转换详解

这个地方报错了,这个地方后面会详细的讲。调用了wait()和notify()方法,它其实是必须要跟一个同步监视器的,而且这个同步监视器所指定的对象必须是当前类的实例,

05-线程的状态以及各状态之间的转换详解

05-线程的状态以及各状态之间的转换详解

05-线程的状态以及各状态之间的转换详解

我们发现一个是等待,一个是唤醒。当处于wait状态的时候它就一直等待,当有了等待之后,那么,当主线程执行到notify()或者notifyAll()之后,wait状态的线程就被唤醒了,于是它处于就绪状态,开始抢夺CPU资源,如果抢到CPU资源,那么就开始执行,本例中执行完之后又处于wait状态,然后接着主线程执行。

这就是关于wait和notify的关系。后面我们会详细的给大家讲解这个问题。这里仅仅是给大家来演示线程的状态。到后面线程之间的通信这些问题会详细的讲。

关于线程之间的状态就说到这里。