作者|开鑫金服-东东(DingXD)

 

 



 


问题:



一个thread在start执行完后,还能再次start吗?





thread.start();
TimeUnit.SECONDS.sleep(1);
thread.start();

连续调用两次start会异常

Exception in thread "main" java.lang.IllegalThreadStateException

原因是thread是有状态的,且有些状态之间的切换是不可逆的

 

劳动人民最光荣



Thread的状态

 

Java thread 外部参数添加_java

 

1、新建状态(New):新创建一个线程对象的初始状态。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于JVM可运行线程池中,变得可运行。该状态下线程等待操作系统调度,获取CPU的使用权后就可以执行线程代码。

3、阻塞状态(BLOCKED):线程在执行过程中,遇到被synchronized关键字保护的代码,等待获得被保护对象的锁(waiting for a monitor lock),此时线程会停止执行。

注意:只有synchronized这种方式的锁(monitor锁)才会让线程进入BLOCKED状态,等待利用cas实现的锁(如ReentrantLock)时,线程仍然处于Runnable状态。

4、等待状态(WAITING or TIMED_WAITING):

让线程进入WAITING只有一种方式:在同步块中,调用锁对象的wait方法,也会让当前持有锁的线程进入wait状态。

public static void timedWaiting() {

        final Object lock = new Object();

        synchronized (lock) {

            try {

                lock.wait();

            } catch (InterruptedException e) {

            }

        }

    }

让线程进入TIMED_WAITING状态,可以给wait方法加一个入参超时时间,或者直接调用线程的thread.join()方法。

处于WAITING状态的线程,只有其他线程调用了锁对象的notify方法才会执行。

处于TIMED_WAITING的线程,除了被锁对象的notify方法唤醒外,到了超时时间会自动唤醒(被join方法阻塞的线程不会被自动唤醒,因为在底层实现中超时时间被设置为0)。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

 

Java thread 外部参数添加_区块链_02

BLOCKED和WAITING状态的区别

 

其实从字面意思就可以看出来:

blocked是过去分词,意思是线程在执行过程中被别人卡住了,即其他线程正在执行这段由synchd保护的代码,等别人执行完自己就会自动执行(jvm调度控制),不需要其他线程唤醒。

而waiting是主动执行wait动作后的当前状态,是主动卡住自己,必须由其他线程调用notify方法才能唤醒。

从下面代码可以方便的理解:

假设t1,t2先后两个线程,都执行如下代码:

synchronized(Obj) {

    Obj.wait();

}

t1先进,最后在Obj.wait()下卡住,这时java管t1的状态waitting状态
t2后进,直接在第一行就卡住了,这时java叫t2为blocked状态。

 Java处于方便管理的考虑,将blocked和waiting状态的线程放到两个队列里(BlockSet和WaitSet),当别的线程运行出了synchronized这段代码,jvm只需要去BlockSet里取,当某人调用了notify(),jvm只需要从WaitSet里取,后面锁机制的章节会详细介绍。

所以两个状态的本质区别是进入和唤醒的条件不一样,其他没有任何区别。

 

劳动人民最光荣



Thread生命周期

 



参考国外网站上一张图,个人认为画的最详细

Java thread 外部参数添加_生命周期_03

 

注意:

1、  Waiting、Block、Runnable之间可以两两切换,而TERMINATED就是终态了,永远不可能在启动了。

2、  处于Runnable状态的线程,并不是立马就执行了,而是等待操作系统的调度启动。且线程再被CPU执行时也不是一口气执行到期,操作系统可能会将线程调出(比如有更高优先级的线程要处理),后续在调入,这个过程对于jvm是透明的。