zoukankan      html  css  js  c++  java
  • 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


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

    赞赏支持

  • 相关阅读:
    Android存储数据方式(转)
    Android实现双进程守护 (转)
    Android DOM、SAX、Pull解析XML(转)
    TCP/IP和Socket的关系(转)
    Socket通信原理和实践
    [转]Android中Intent传递对象的两种方法(Serializable,Parcelable)
    内存堆和栈的区别
    hdu 1754 线段树
    hdu 1166 线段树
    zoj 3686 线段树
  • 原文地址:https://www.cnblogs.com/east7/p/14454038.html
Copyright © 2011-2022 走看看