Java线程的6种状态以及多线程的上下文切换的概念【自写的暂未出版书籍部分章节整合/节选】

编程

2.1  线程的状态

        线程的状态是线程的基本属性。在Java中,线程一共分为6种状态,本节内容将会简单介绍这6种状态,以及在程序中如何获取一个线程的当前状态等。

2.1.1  线程的6种状态

我们可以查阅Thread的源代码来了解,Java中线程的一些状态:

public class Thread implements Runnable {

/* Make sure registerNatives is the first thing <clinit> does. */

private static native void registerNatives();

static {

registerNatives();

}

private volatile char  name[];

private int            priority;

private Thread         threadQ;

private long           eetop;

……//省略部分代码

/**

* A thread state.  A thread can be in one of the following states:

* <ul>

* <li>{[@link](https://my.oschina.net/u/393) #NEW}<br>

*     A thread that has not yet started is in this state.

*     </li>

* <li>{[@link](https://my.oschina.net/u/393) #RUNNABLE}<br>

*     A thread executing in the Java virtual machine is in this state.

*     </li>

* <li>{[@link](https://my.oschina.net/u/393) #BLOCKED}<br>

*     A thread that is blocked waiting for a monitor lock

*     is in this state.

*     </li>

* <li>{[@link](https://my.oschina.net/u/393) #WAITING}<br>

*     A thread that is waiting indefinitely for another thread to

*     perform a particular action is in this state.

*     </li>

* <li>{[@link](https://my.oschina.net/u/393) #TIMED_WAITING}<br>

*     A thread that is waiting for another thread to perform an action

*     for up to a specified waiting time is in this state.

*     </li>

* <li>{@link #TERMINATED}<br>

*     A thread that has exited is in this state.

*     </li>

* </ul>

*

* <p>

* 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 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:

* <ul>

*   <li>{@link Object#wait() Object.wait} with no timeout</li>

*   <li>{@link #join() Thread.join} with no timeout</li>

*   <li>{@link LockSupport#park() LockSupport.park}</li>

* </ul>

*

* <p>A thread in the waiting state is waiting for another thread to

* perform a particular action.

*

* For example, a thread that has called <tt>Object.wait()</tt>

* on an object is waiting for another thread to call

* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on

* that object. A thread that has called <tt>Thread.join()</tt>

* 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:

* <ul>

*   <li>{@link #sleep Thread.sleep}</li>

*   <li>{@link Object#wait(long) Object.wait} with timeout</li>

*   <li>{@link #join(long) Thread.join} with timeout</li>

*   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>

*   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>

* </ul>

*/

TIMED_WAITING,

/**

* Thread state for a terminated thread.

* The thread has completed execution.

*/

TERMINATED;

}

/**

* 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 sun.misc.VM.toThreadState(threadStatus);

}

……//省略部分代码

/* Some private helper methods */

private native void setPriority0(int newPriority);

private native void stop0(Object o);

private native void suspend0();

private native void resume0();

private native void interrupt0();

private native void setNativeName(String name);

}

上面的一段Thread源代码,介绍了Thread的一些状态定义,其使用了enum(枚举)列举出了所有的状态,参考代码如下:

public enum State {

NEW,

RUNNABLE,

BLOCKED,

WAITING,

TIMED_WAITING,

TERMINATED;

}

可以看出在Java中,一共定义了6种线程的基本状态:

(1)新建(NEW):线程创建成果,但还没有调用start( )方法启动

(2)可运行(RUNNABLE):线程处于可执行的状态,表明该线程已经在Java虚拟机中执行,但可能它还在等待操作系统例如CPU等其他资源。

(3)阻塞(BLOCKED):线程处于阻塞,其实是指该线程正在等待一个监控锁,特别是在多线程下的场景等待另一个线程同步块的释放,如出现线程synchronized多线程同步关键字修饰的同步块或者方法中,或者之前调用了wait( )方法,后来被notify( )通知唤醒时重新进入synchronized多线程同步关键字修饰的同步块当中。

(4)等待(WAITING):线程出于等待状态,指的是该线程正在等待另一个线程执行某些特定的操作。譬如A线程调用了wait( )方法,进入等待状态,实际上该线程可能是等待B线程调用notify( )方法或notifyAll( )方法。又或者是A线程使用了C线程的join( )方法,所以按顺序在等待C线程的结束等。

(5)调校时间的等待(TIMED_WAITING):线程的这个状态与等待状态不同,这种是与时间相关的等待,一般是调用了某些设定等待时长参数的方法,导致该线程按该调校的时间进行等待。譬如sleep(500)或wait(1000)等。

(6)终止(TERMINATED):线程执行完毕的状态。

这里已经简单介绍完Java线程的6种状态,读者可能会一时间未能完全明白,但可以先大致记忆这6种状态,在后面的章节中,还会通过其他内容对这6种状态从侧面对加以分析。

2.1.2  线程状态的获取方法

可以在程序中使用某个线程的getState( )方法得到该线程目前这一刻的状态。实际上在多线程的环境下,一个线程的状态可能会在极短的时间内变化多次。先以一个简单的例子来看看一个线程的状态的变化,改写之前的ProgressBar01这个线程类的总的执行入口main( )方法。

参考代码如下:

public class ProgressBar01 extends Thread {

private int progressValue = 0; //进度条的目前进度值

private int accValue = 0; //累加辅助器,初始值为0;

@Override

public void run(){

for (int i = 0; i <= 300; i++){

System.out.println("我已经数到了第" + i + "个数字了哟, 目前进度:"

+ progressValue + "%");

accValue++;

if (accValue ==3){

progressValue++;

accValue = 0;

}

}

}

public static void main(String[] args) {

ProgressBar01 countProgressBar = new ProgressBar01();

System.out.println("数数线程刚创建,还没有调用start()方法,它这时的状态是:"

+ countProgressBar.getState());

countProgressBar.start();

System.out.println("数数线程调用了start()方法,它这时的状态是:"

+ countProgressBar.getState());

try {

Thread.sleep(2);

System.out.println("主线程等待2毫秒,数数线程应在执行任务,它这时的状态是:"

+ countProgressBar.getState());

Thread.sleep(2000);

System.out.println("主线程等待2秒,数数线程应该已完成任务,它这时的状态是:"

+ countProgressBar.getState());

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

运行后的参考结果如下:

数数线程刚创建,还没有调用start()方法,它这时的状态是:NEW

数数线程调用了start()方法,它这时的状态是:RUNNABLE

我已经数到了第0个数字了哟, 目前进度:0%

我已经数到了第1个数字了哟, 目前进度:0%

我已经数到了第2个数字了哟, 目前进度:0%

我已经数到了第3个数字了哟, 目前进度:1%

我已经数到了第4个数字了哟, 目前进度:1%

我已经数到了第5个数字了哟, 目前进度:1%

我已经数到了第6个数字了哟, 目前进度:2%

我已经数到了第7个数字了哟, 目前进度:2%

我已经数到了第8个数字了哟, 目前进度:2%

我已经数到了第9个数字了哟, 目前进度:3%

我已经数到了第10个数字了哟, 目前进度:3%

我已经数到了第11个数字了哟, 目前进度:3%

我已经数到了第12个数字了哟, 目前进度:4%

我已经数到了第13个数字了哟, 目前进度:4%

我已经数到了第14个数字了哟, 目前进度:4%

我已经数到了第15个数字了哟, 目前进度:5%

我已经数到了第16个数字了哟, 目前进度:5%

我已经数到了第17个数字了哟, 目前进度:5%

我已经数到了第18个数字了哟, 目前进度:6%

我已经数到了第19个数字了哟, 目前进度:6%

我已经数到了第20个数字了哟, 目前进度:6%

主线程等待2毫秒,数数线程应在执行任务,它这时的状态是:RUNNABLE

我已经数到了第21个数字了哟, 目前进度:7%

我已经数到了第22个数字了哟, 目前进度:7%

我已经数到了第23个数字了哟, 目前进度:7%

……

我已经数到了第296个数字了哟, 目前进度:98%

我已经数到了第297个数字了哟, 目前进度:99%

我已经数到了第298个数字了哟, 目前进度:99%

我已经数到了第299个数字了哟, 目前进度:99%

我已经数到了第300个数字了哟, 目前进度:100%

主线程等待2秒,数数线程应该已完成任务,它这时的状态是:TERMINATED

通过这个简单的例子,我们可以看到,数数线程在非常短的时间内,就发送了多次的状态变化。例如在刚创建好线程还没有启动时,数数线程的状态是NEW;当main线程立刻调用数数线程的start( )方法时,数数线程的状态变成了RUNNABLE;当数数线程刚开始工作不久,数到某个数字时,也就是数数线程正在运行中时,其状态也是RUNNABLE。当数数线程的工作完毕后,数数线程的状态变为了TERMINATED。

这个需要强调一下,Java的线程不能单独存在RUNNING这一个状态值,实际上运行中Java线程,其状态就是与使用了start( )方法之后的RUNNABLE状态相同,即RUNNABLE状态。

2.1.3  线程的活动情况获取方法

对于Java的线程运行情况的描述,除了获取状态的方法外,还有一个isAlive( )方法可以获得线程的活动情况,可以用于判断一个线程是否还活动(存活)。该方法返回的值为true或false,并不返回线程当前状态,但可以作为一种线程的描述方法。适当地修改上面的示例,加入isAlive( )方法。

参考代码如下:

public class ProgressBar01 extends Thread {

private int progressValue = 0; //进度条的目前进度值

private int accValue = 0; //累加辅助器,初始值为0;

@Override

public void run(){

for (int i = 0; i <= 300; i++){

System.out.println("我已经数到了第" + i + "个数字了哟, 目前进度:"

+ progressValue + "%");

accValue++;

if (accValue ==3){

progressValue++;

accValue = 0;

}

}

}

public static void main(String[] args){

ProgressBar01 countProgressBar = new ProgressBar01();

System.out.println("数数线程刚创建,还没有调用start()方法,它这时的状态是:"

+ countProgressBar.getState());

System.out.println("数数线程的活动情况:" + countProgressBar.isAlive());

countProgressBar.start();

System.out.println("数数线程调用了start()方法,它这时的状态是:"

+ countProgressBar.getState());

System.out.println("数数线程的活动情况:" + countProgressBar.isAlive());

try {

Thread.sleep(2);

System.out.println("主线程等待2毫秒,数数线程应在执行任务,它这时的状态是:"

+ countProgressBar.getState());

System.out.println("数数线程的活动情况:" + countProgressBar.isAlive());

Thread.sleep(2000);

System.out.println("主线程等待2秒,数数线程应该已完成任务,它这时的状态是:"

+ countProgressBar.getState());

System.out.println("数数线程的活动情况:" + countProgressBar.isAlive());

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

运行后的参考结果如下:

数数线程刚创建,还没有调用start()方法,它这时的状态是:NEW

数数线程的活动情况:false

数数线程调用了start()方法,它这时的状态是:RUNNABLE

数数线程的活动情况:true

我已经数到了第0个数字了哟, 目前进度:0%

我已经数到了第1个数字了哟, 目前进度:0%

我已经数到了第2个数字了哟, 目前进度:0%

我已经数到了第3个数字了哟, 目前进度:1%

主线程等待2毫秒,数数线程应在执行任务,它这时的状态是:RUNNABLE

数数线程的活动情况:true

我已经数到了第4个数字了哟, 目前进度:1%

我已经数到了第5个数字了哟, 目前进度:1%

我已经数到了第6个数字了哟, 目前进度:2%

我已经数到了第7个数字了哟, 目前进度:2%

……

我已经数到了第292个数字了哟, 目前进度:97%

我已经数到了第293个数字了哟, 目前进度:97%

我已经数到了第294个数字了哟, 目前进度:98%

我已经数到了第295个数字了哟, 目前进度:98%

我已经数到了第296个数字了哟, 目前进度:98%

我已经数到了第297个数字了哟, 目前进度:99%

我已经数到了第298个数字了哟, 目前进度:99%

我已经数到了第299个数字了哟, 目前进度:99%

我已经数到了第300个数字了哟, 目前进度:100%

主线程等待2秒,数数线程应该已完成任务,它这时的状态是:TERMINATED

数数线程的活动情况:false

可以看出,线程的活动情况,与线程的RUNNABLE状态基本一致,也是在线程start( )方法启动后为true,当线程结束时,线程的活动情况变为false。

2.2  线程生命周期概念

Java线程的生命周期包含Java的6种基本状态,它们按一定的顺序和条件进行转换。Java线程的生命周期如同人的生命周期相似,有一定的趋势,也有一些不可逆的过程。本节将介绍Java线程的生命周期。

2.2.1  线程的生命周期图谱

通过2.1的学习,我们可以知道Java的线程有6种状态,这些状态之间按一定的规则联系,线程的状态会转变,这些状态间的转变有一定的规律和顺序。线程的活动就像人经历成长一样,有始有终,有一个生命周期,所以可以将一个线程的活动总结成线程的生命周期图谱,如图2.1所示。

图2.1  线程的生命周期图谱

图2.1中,圆角矩形一共有6个,每一个圆角矩形对应Java线程的一个状态。可以看出,线程的生命周期,由NEW状态开始,由TERMINATED状态结束。其中RUNNABLE状态中包含了READY(就绪)、RUNNING(运行中)子状态,也就是说,READY、RUNNING都属于RUNNABLE状态。

当一个线程一开始调用start( )方法时,线程就会处于RUNNABLE状态,但可能一开始未得到CPU资源,所以会处于READY子状态,但一旦得到相关的CPU资源,则会进入RUNNING子状态。

注意,READY和RUNNING子状态并非是Java线程所定义的6大状态之一,也无法通过相关方法得到READY、RUNNING的值,这里只是对Java的RUNNABLE状态加以细分说明。线程处于RUNNABLE状态时,有可能会与WAITING、TIMED_WAITING、BLOCKED状态相互转换。当线程完成所设定的任务后,最终会到达TERMINATED状态,完成自己的使命,线程的生命周期结束。

2.2.2  多线程的上下文环境切换

阅读相关的技术文献时经常会出现“上下文”一词,上下文是计算机的专业术语,是对国外计算机文献常用词汇context的中文翻译。该词汇由字面上来看,非常难于理解,上下文,到底何为上,何为下,而文又是什么?对于初学者来说,“上下文”一词非常抽象,甚至查阅了许多的资料和文献,也未能给出一个非常易于理解的概念。

由于对context的中文替代词--上下文进行下定义和解释都非常的抽象,但为了让读者多一个参考,笔者也适当地给出一个解释,说明context的中文翻译“上下文

”一词的含义。

上下文是一种特定的计算机语境词,是特定环境的抽象化,其常用于命名程序代码中的某个全局变量,或某作用域下的一种变量,该类变量能对某环境下的数据和配置进行管理,或者作为中间的重要环节,起到上层和下层内容的连接或交互,其可以贯穿某事物生命周期的上上下下、方方面面,所以称之为上下文。

如果觉得“上下文”一词太过难意会,不妨将其扩展为另外几个词汇来帮助理解,如上下文环境、上下文容器、上下文信息(其中,“上下文环境”一词,几乎可以在多种场景下表达出context的含义,所以本书后面对context的中文翻译,将直接使用上下文环境替代上下文一词)。例如一些企业级开发中经常用到的spring框架的ApplicationContext,就是一个上下文环境对象,它包含了整个spring项目的许多系统信息、对象信息和配置信息等,可以贯穿整个spring项目,让用户可以方便得到。

同样,每一个线程都有其自己的context,我们称之为线程的上下文环境。当在多线程的情况下,线程之间就可能会发生上下文环境切换。特别的,结合之前学习的线程的生命周期来看,当多线程的情况下,当其中一个线程由RUNNABLE状态转为BLOCKED、WAITING、TIMED_WAITING状态时,就会发生线程间的上下文环境切换。

我们可以给出这样的定义:线程的上下文环境切换是指在多线程的情况下,如果其中的一个线程进入了BLOCKED(阻塞)、WAITING(等待)以及TIMED_WAITING(调校时间的等待)的状态下,这时由另外的一个线程切换介入的过程就可以称之为线程的上下文切换。

以上是 Java线程的6种状态以及多线程的上下文切换的概念【自写的暂未出版书籍部分章节整合/节选】 的全部内容, 来源链接: utcz.com/z/513237.html

回到顶部