对比java线程Thread,闲聊线程状态

我们知道,线程的生命周期分为5种生命状态:新建,就绪(可运行),运行,等待,死亡,这些状态几乎是所有线程都必然会经历的。之前一次面试,一个面试官问我,java的线程类Thread,有几种状态?5种啊!面试官笑了笑。。反正那次面试没过。

虽然感觉自己回答的没问题,回来还是查了下,曰!Thread的线程状态是TMD的6种,当时就有一种感觉,这是被降维打击了么,还没明白题目的意思,就被直接KO!后来仔细对比了下线程状态,在这里说一说。

线程的生命周期:

1.新建(new):新创建一个线程对象。类似new Thread();

2.就绪(RUNNABLE):线程创建对象后,其他线程调用了该对象的start()方法,该状态的线程位于可运行的线程池中,等待被线程调度选中,获取CPU使用权。调用start()方法之后不一定会启动,要看线程资源的分配的哦。

3.运行(RUNNING):可运行状态的线程获得了CPU的时间片,执行程序代码。简单点说,就是就绪状态的线程获得了CPU资源,执行方法run(),此时线程便处于运行状态。运行状态的线程可能直接死亡(宕机),阻塞(其他线程join,本线程sleep,synchronized方法等),转变为就绪(wait+notify)

4.阻塞(BLOCKED):线程放弃了 CPU使用权,让出了CPU时间片(timeslice),暂时停止运行。比如一个线程执行了sleep(),suspent(挂起)等方法,失去占有的资源之后,该线程就从运行状态进入阻塞状态。之后线程必须在进入可运行状态,才有机会获得CPU使用权,重新进入运行状态。阻塞状态分为三种:

            等待阻塞:运行状态中的线程执行了wait()方法,使线程进入了阻塞状态。

            同步阻塞:线程在获取synchronized同步锁失败(其他线程抢占了synchronized锁,占有了监视器)。

            其他状态:通过本线程调用sleep(),或者其他线程调用join(),发出I/O请求,线程进入阻塞状态,当sleep()超时或者join()方法执行完毕或者超时,线程重新转换为就绪状态。

5.死亡(DEAD):一个运行状态的线程完成任务(run()方法执行结束)或者因某种条件终止时,该线程就切换为死亡状态,该线程生命周期结束,不会再有状态的改变。

线程状态流转图

 

类似上图的线程流转图大伙可能都看到过,这里就不多说啦。

我们下面来看下java中的Thread的状态有哪些。

1.NEW(新建)

就是继承Thread或者实现一个runnable方法,创建一个线程。此时,JVM为其分配内存,并初始化成员变量的值。但是线程对象并没有表现出任何线程的动态特征,程序也不会执行线程的线程执行体。

2.RUNNABLE

可运行线程的线程状态,一个可以运行的线程的状态,可以运行是指这个线程已经在JVM中运行了,但是有可能正在等待其他的系统资源,例如处理器。这个时候,JVM会为线程创建方法调用栈和程序计数器。

3.BLOCKED

处于运行状态的线程在某些情况下,让出CPU并暂时停止自己的运行,进入阻塞状态。比如,一个线程发起一个阻塞式I/O,或者申请一个已经被其他线程占有的资源时,线程就会处于该状态。处于BLOCKED状态的线程不会占有处理器资源,当阻塞式I/O操作完成或者申请到了资源之后,该线程又会转换为RUNNABLE状态。

4.WAITING

一个线程执行了某些特定的方法之后就会处于这种等待其他线程执行操作的状态。比如执行了Object.wait(),其他线程join,或者LockSupport.park(Object)。

5.TIMED_WAITING

这个状态和WAITING类似,差别在于处于该状态的线程并非无限制的等待其他线程执行特定操作唤醒,而是处于有时间限制的等待状态。当其他线程没有在特定时间内执行该线程所期望的特定操作时,该线程自动切换成RUNNABLE状态。

6.TEMINATED

已经执行结束的线程处于该状态。由于一个线程实例只能被启动一次,所以一个线程也只有一次该状态.Thread.run()正常返回或者由于抛出异常而提前终止都会导致线程处于该状态。

java编程艺术中很经典的一张图

下面我们来看下这张图哈,把这些都搞懂了,线程之间的转换应该没太大问题了,O(∩_∩)O~

1.Thread.start() ,一个线程创建后启动,调用start方法,相当于新建线程调用run方法,直接调用run方法的话,那就是本线程调用了。

2.yield().线程调用这个方法,对于Thread来说,状态还是RUNNABLE,只是让出了时间片资源,一旦系统调度使其获得了资源,线程就会继续执行。对比线程中的五种状态,那调用了这个方法,就是使线程进入了就绪状态。

3.Object.wait(),这里说下,wait()是Object类自带方法哦,有兴趣的伙伴可以看下Object,有助于理解java对象的哈。线程调用wait()方法,如果不设置时间,Thread就进入了WAITING状态,设置时间的话就进入了TIMED_WAITING状态,这个对应于线程的状态,应该是阻塞,没法再进行running了。

4.Object.join 大家对join不是很熟悉的话可以去看下我之前写的 谈谈对java线程的理解(二)--------关键字join。比如,一个main()方法正在执行,一个线程 Thread.join() 进来,当前main()方法的线程就会等待,直到join进来的线程执行完毕。

5.LockSupport.part(thread)   thread线程等待,只有执行LockSupport.unPark(Thread),许可线程执行,才会继续执行。

6.Object.notify 唤醒等待的线程,这个时候线程重新变成RUNNABLE状态,等待CUP调度,获取资源。

7.Object.notifyAll() 唤醒所有wait的线程,类似notify。

8.LockSupport.unpark(Thread)  允许运行,许可,就是解除LockSupport.part(thread) 的等待。这个在ReentrantLock中加锁,释放锁中有使用,有兴趣的可以去看下。

9.Thread.sleep(long) ,这个是直接进入TIMED_WAITING状态,不过要注意的是sleep()方法并不会让出占有的资源控制权,只是让线程一直在那等待,如果是wait(long)的话,是会让出的。

10.Object.wait(long),也是状态进入TIMED_WAITING。

11.LockSupport.parkNanos()、LockSupport.parkUtil() 这两个方法也是直接进入TIMED_WAITING状态,不过没怎么见过,有兴趣的伙伴可以去学习下。

12.synchronized方法,等待获取监视器锁,线程阻塞,获取到锁之后,线程重新进入RUNNABLE状态。

java中所有的线程、并发处理都离不开线程状态的控制,今天就说道这里吧,接下来有时间的话可以尽量写一个线程状态转换的测试方法,有兴趣的伙伴可以试一试哈。

No sacrifice,no victory~

 

猜你喜欢

转载自blog.csdn.net/zsah2011/article/details/109305867