Java线程生命周期

本节主要讲解线程的六种状态以及状态之间的转换,理解了这个知识点对多线程的编程工作将会大大有益。

Thread的六种状态

  • 线程的任何生命周期在任何时刻只能处于一种状态,线程的状态可以通过getState()方法获取,State是一个枚举类,位于Thread中,六种状态可以理解为它的生命周期,源码如下:
public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * 
    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
  • NEW: 说明线程刚处于新建状态,还没有调用start方法,线程生命周期第一个状态。
  • RUNNABLE: 可运行状态,如果线程抢夺到了CPU的时间片,就会执行,否则就等待。
  • BLOCKED:被阻塞状态,主要是因为没有抢夺到synchronized的锁导致阻塞。
  • WAITING: 等待状态,比如调用了object.wait(),或者thread.join()导致的等待。
  • TIMED_WAITING: 计时等待,和上面的差不多,不过多了一个计时而已,在有效的时间结束后,线程会被激活。
  • TERMINATED: 被终止状态,线程生命周期最后的一个状态。

线程六种状态的详细图解

  • 1 NEW状态
    Java线程生命周期_第1张图片

  • 从图中我们可以看出来,New状态时线程生命周期的第一个状态。当调用new Thread()创建线程的时候,线程就处于NEW状态,如果调用了线程的start()方法,线程状态就会从NEW状态变为Runnable状态。而且NEW状态在Thread生命周期中只出现一次。

  • 2 RUNNABLE状态
    Java线程生命周期_第2张图片

  • Runnable状态其实并不一定是在运行的状态,它内部包含了两种状态,真正的运行态Running和准备运行态Ready,当线程获取到了CPU的时间片之后就会处于Running状态,如果处于Running状态的线程的cpu被调度去做其它的任务了,线程就处于Ready状态,等到再次获取到CPU的时间片执行的时候,线程就又处于Running状态了。但是不管是Running还是Ready状态,总的来说都是Runnable状态。

  • 3 BLOCKED阻塞状态
    Java线程生命周期_第3张图片

  • 想要进入BLOCKED状态只能是从Runnable状态进来的,New状态只能进入到Runnable状态。并且线程进入Blocked状态的条件只能是进入synchronized修饰的代码块或者方法,但是并没有获取到monitor锁,就会处于Blocked阻塞状态。当此线程获取到monitor锁之后就会从Blocked状态进入Runnable状态。

  • 4 WAITING状态
    Java线程生命周期_第4张图片

  • 进入Waiting状态的条件有如下几种:

    • object.wait()
    • thread.join()
    • LockSupport.park(this)。
  • 在Blocked状态提到了,只有使用synchronized的方式才能进入Blocked状态,如果其它的加锁方式呢?比如我们熟知的ReentrantLock.await()或者CountDownLatch.await()方式,查看源码很容易发现使用的都是LockSupport.park()的方式。都会进入Waiting状态。

  • 这里Blocked和Waiting状态就可以有一些对比了,Blocked状态是线程在等待其它线程释放锁,而Waiting则是在等待某一个条件,比如object.wait()的条件是object.notify()或者object.notifyAll(),如果没有这些条件唤醒线程的话,可能线程一直处于阻塞状态,所以使用的时候要格外小心。如果是thread.join()的话,就需要插入的线程执行结束才可能触发线程。

  • 5 TIMED WAITING状态
    Java线程生命周期_第5张图片

  • TIMED WAITING状态和WAITING状态的区别就是多了一个时间的限制而已,当时间超时的话,就会自动由系统唤醒线程,或者被唤醒信号唤醒。

  • 想要进入TIMED WAITING状态有几种方式:

    • object.wait(time)
    • Thread.sleep(time)
    • thread.join()
    • LockSupport.parkNanos(time)
    • LockSupport.parkUntil(time)
  • 6 TERMINATED状态
    Java线程生命周期_第6张图片

  • 想要进入TERMINATED状态,有两种方式,要么是程序正常运行结束,run方法执行完毕。

  • 要么是异常结束,中断了run方法了执行,导致程序结束。

线程状态之间的转换

  • 上面详细图解讲述了线程的六种状态,下面我们分析一下线程之间状态的转换。
  • Runnable和Blocked状态之间的转换
    Java线程生命周期_第7张图片
  • 看上图红色部分,如果线程进入synchronized修饰的代码块或者方法的话,但是没有获取到monitor的话就会进入Blocked状态;如果获取了monitor之后,就会再次进入Runnable状态。
  • Runnable和Waiting以及Blocked之间的转换
    Java线程生命周期_第8张图片
  • 从上图红色区域可以看出,进入waiting状态的线程,下一个状态可能为Blocked,也可能是Runnable。如果join线程执行结束或者被中断,或者调用LockSupport.unpark(this)可以进入Runnable状态。
  • 如果其它线程调用了notify()或者notifyAll()来唤醒需要唤醒的线程,则会直接进入Blocked状态,这是为什么呢?因为线程想要调用notify或者notifyAll()必须得持有monitor锁,当调用notify或者notifyAll()方法的时候,所在线程持有的monitor锁并不一定立马就释放,只有当释放了锁,并且被激活的线程得到了monitor锁才会从Blocked状态回到Runnable状态。
  • 我们用一个案例来验证上述情况,如下,当在线程2中调用obj.notify()方法之后,线程2并不是立马就释放锁,线程1也不是立马就获得锁。此时线程1只是从Waiting状态变为了Blocked状态,当线程2执行结束之后,线程1得到锁后,线程1才会从Blocked进入Runnable状态,所以运行的结果中,thread2 notify finish永远早于thread1 wait finish
    fun main() {
        val obj = Object()
        thread(name = "thread1") {
            println("thread1 waiting to get lock")
            synchronized(obj) {
                println("thread1 get lock")
                println("thread1 begin wait")
                Thread.sleep(1000)
                obj.wait()
                println("thread1 wait finish")
            }
        }
    
        thread(name = "thread2") {
            println("thread2 waiting to get lock")
            synchronized(obj) {
                println("thread2 get lock")
                println("thread2 begin notify")
                obj.notify()
                println("thread2 notify finish")
            }
        }
    }
    //运行结果
    thread1 waiting to get lock
    thread1 get lock
    thread1 begin wait
    thread2 waiting to get lock
    thread2 get lock
    thread2 begin notify
    thread2 notify finish
    thread1 wait finish
    
    
  • Timed Waiting状态的转换和Waiting差不多,就是多加了一个超时的机制,当超时时间到了并且能获取到monitor的话就会进入Runnable状态。

总结

  • 线程的生命周期是不可逆的
  • 只有一次New和Terminated状态
  • 线程的生命周期是按照本篇图中箭头的指向运行的,只有处于中间状态的可以相互转换。

你可能感兴趣的:(Java线程生命周期)