Java线程状态和状态切换

背景

  先来探讨一个关于多线程的基础知识:java线程有多少种状态?根据JDK定义,答案是六种!为什么很多人给出的答案却是五种呢?这极有可能是将操作系统层面的线程状态和Java线程状态混为一谈了。因此,小编在翻阅JDK源码的基础上,介绍一下java线程的六种状态以及操作系统层面的五种状态,欢迎拍砖。

java线程状态

  JDK中声明了六种Java线程状态,以枚举类的形式定义在Thread.State中,而且注释开篇撇清了和操作系统层面线程状态的关系——【这些状态是虚拟机状态,不反映任何操作系统的线程状态】,英文原文描述如下:

        /**
	 * A thread can be in only one state at a given point in time.
	 * These states are virtual machine states which do not reflect
	 * any operating system thread states.
	 *
	 * @since   1.5
	 * @see #getState
	 */
    public class Thread implements Runnable {
	public enum State {
		NEW,
		RUNNABLE,
		BLOCKED,
		WAITING,
		TIMED_WAITING,
		TERMINATED;
	}
}

  简单来介绍一下这6种状态。

  1))NEW:新建状态,新创建一个线程对象时的初始状态,此时尚未调用 start() 方法。

  2)RUNNABLE:就绪状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“就绪状态或者可运行状态”。英文相关描述如下:

  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.

  线程对象创建后,其它线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。

  3)BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了 CPU 使用权,阻塞也分为几种情况 :

  • 等待阻塞:运行的线程执行了 Thread.sleep 、Object.wait()、 join() 等方法,JVM 会把当前线程设置为等待状态,当 sleep 结束、join 线程终止或者线程被唤醒后,该线程从等待状态进入到阻塞状态,重新抢占锁后进行线程恢复。

  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其它线程锁占用了,那么JVM会把当前的线程放入到锁池中。

  • 其他阻塞:发出了 I/O请求时,JVM 会把当前线程设置为阻塞状态,当 I/O处理完毕则线程恢复。

  4)WAITING:等待状态,没有超时时间,要被其它线程或者有其它的中断操作;即一个正在无限期等待另一个线程执行一个特别的动作的线程处于WAITING状态,英文原文如下:

A thread that is waiting indefinitely for another thread to perform a particular action is in this state.

一个线程进入 WAITING 状态是因为调用了方法Object.wait(), Thread.join()或者LockSupport.park()。然后会等其它线程执行一个特别的动作,比如:

  • 一个调用了Thread.join方法的线程会等待指定的线程结束。
  • 一个调用了某个对象的wait方法的线程会等待另一个线程调用此对象的notify() 或 notifyAll()。

  5)TIMED_WAITING:超时等待状态,超时以后自动返回;如下方法执行超时,就会进入超时等待状态:Thread.sleep(long)、Object.wait(long)、Thread.jjoin(long)、LockSupport.park(long)、LockSupport.parkNanos(long)、LockSupport.parkUntil(long)。

  6)TERMINATED:终止状态,表示当前线程执行完毕 。

  我们可以通过函数getState()来查看线程的当前状态:

    /**
     * Returns the state of this thread.
     * This method is designed for use in monitoring of the system state,
     * not for synchronization control.
     *
     * @return this thread's state.
     * @since 1.5
     */
    public State getState() {
        // get current thread state
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }

线程状态间的转换

  借一个图来描述:

  关于具体的转换场景,图中描述的比较清楚,此处不再赘述。注意:

  1)sleep、join、yield时并不释放对象锁资源,而执行函数wait()时会释放锁,对象在被notify/notifyAll唤醒时,重新去抢夺获取对象锁资源。

  2)sleep可以在任何地方使用,而wait,notify,notifyAll只能在同步方法或者同步块中使用。

  3)调用obj.wait()会立即释放锁,以便其他线程可以执行notify(),但是notify()不会立刻立刻释放sycronized(obj)中的对象锁,必须要等notify()所在线程执行完同步方法或者同步块才会释放这把锁,然后供线程等待池的线程来抢夺对象锁。

  wait方法是Object的方法,线程释放锁,进入WAITING或TIMED_WAITING状态。等待时间到了或被notify/notifyAll唤醒后,回去竞争锁,如果获得锁,进入RUNNABLE,否则进入阻塞状态等待获取锁。

操作系统层面线程状态

  很多人会把操作系统层面的线程状态与java线程状态混淆,所以导致有的文章中把java线程状态写成是5种,在此我们说清楚一个问题,java线程状态是6个,操作系统层面的线程状态是5种,如下图所示:

  下面分别介绍一下这5种状态:

  1)new :一个新的线程被创建,等待该线程被调用执行。

  2)ready :表示线程已经被创建,正在等待系统调度分配CPU使用权或者时间片已用完,此线程被强制暂停,等待下一个属于它的时间片到来。

  3)running :表示线程获得了CPU使用权,正在占用时间片。

  4)waiting :表示线程等待(或者说挂起),等待某一事件(如IO或另一个线程)执行完,让出CPU资源给其他线程使用。

  5)terminated :一个线程完成任务或者其它终止条件发生,该线程终止进入退出状态,退出状态释放该线程所分配的资源。

  需要注意的是,操作系统中的线程除去new 和terminated 状态,一个线程真实存在的状态是ready 、running和waiting 。

  Thread.State 中的RUNNABLE 状态涵盖了 操作系统层面的【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行,就好比我们在run()方法里调用IO方法,再者虽然有线程上下文切换但在JAVA层面还是运行的)。

小结

  关于Java线程池状态及其切换,各位同仁可以参考《Java线程池状态和状态切换》。

  至此,我们就把java线程状态以及操作系统层面的线程状态厘清了,是不是以后再也不会混淆了?希望能帮助到大家。

Reference


  读后有收获,小礼物走一走,请作者喝咖啡。

赞赏支持

原文地址:https://www.cnblogs.com/east7/p/14454038.html