线程的生命周期及管理线程类Thread
3423字,阅读需时12分钟

文章导读

Thread类是线程管理类,用于管理线程的启动、停止、中断、设置线程优先级等活动。本文结合线程的生命周期和状态,介绍Thread类管理线程的诸多方法。


前面对线程有了基本认识,初步掌握了线程的使用方法。但还有些问题没有解决,线程从创建到销毁由谁来管理?期间经历了那些状态?线程的生命周期是怎样的?带着这些问题,下面的内容分成三个个节。第一小节主要介绍线程的声明周期及状态;第二小节介绍线程的上下文切换;第三小节讲述线程管理类Thread类。

第一小节 线程的生命周期及状态

线程从创建到消亡,要经历创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、等待(waiting)、消亡(dead)等若干状态,其中运行、阻塞、等待状态之间会互相转换。

线程创建后,不会立即进入就绪状态,需要等待JVM分配线程运行所必备的资源,如内存资源、运行栈、程序计数器等。待所有资源分配完毕后,线程进入就绪状态。

进入就绪的线程,需要等待JVM的调度以获取CPU的时间,对于单核CPU来说,当前时间段只能运行一个线程,如果是多核CPU,可以运行多个线程。线程得到CPU的执行时间后,就进入了运行状态。

进入运行状态的线程,可能会由于多种原因导致线程不能继续运行下去。例如,线程自身进入睡眠状态、或者被其它线程阻塞、或者被JVM调度进入等待状态等。此时就对应着多个状态:time waiting(在一定时间内等待被唤醒)、waiting(等待被唤醒)、blocked(阻塞)。

线程执行完毕,JVM会清理线程所使用到的资源,线程进入消亡状态。

线程从创建到消亡状态之间的转换如下图所示:

blob.png

图 1 线程从创建到消亡过程状态转换图

 

第二小节 线程的上下文切换

前面说到线程需要轮换使用CPU的时间,当正在运行的线程被JVM切换到另一个线程时,需要记录当前线程的运行栈、程序计数器等数据和线程状态,当线程再次被唤醒时,能够保持当前的状态继续执行。这种过程称为线程的上下文切换。

举个例子,假如线程A正在读取文件内容,读到一半时,JVM需要暂停线程A,转去执行线程B,当再次切换回来执行线程A的时候,需要保持线程A读取文件的状态。

对于线程的上下文切换实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。

第三小节  Thread类的主要方法

Thread是主要的线程管理类,用于管理线程的启动、中断、设置线程优先级、获取当前运行的线程对象等活动。下面给出Thread类的主要方法说明。

●  public void start ()

用于启动线程,需要注意的是,在某个线程上调用这个方法后,它还需要看JVM是否调度到该线程,只有调度到该线程,它才运行。

●  public static Thread currentThread ()

用于获取当前运行的线程对象。

●  public ClassLoader getContext ClassLoader ()

用于返回当前线程的上下文ClassLoader。上下文ClassLoader由线程创建者提供,供运行于该线程中的代码在加载类和资源时使用。

●  public final boolean isAlive ()

用于判断线程是否还存在。

●  public Thread.State getState ()

用于获取当前线程的状态。

●  public final String getName ()

用于获取当前线程的名称。

●  public final void setName ()

用于设置当前线程名称。

●  public final void setDaemon (boolean on)

用于将该线程标记为守护线程或用户线程。

●  public final void setPriority (int newPriority)

用于更改当前线程的优先级,优先级高的抢占资源的概率要高一些,同样的优先级在前面的调度的更快。

●  public static void sleep (long millis) throws InterruptedException

用于在指定的毫秒内让当前正在执行的线程休眠(暂停执行)。

●  public void interrupt ()

用于中断当前线程。

Thread类方法使用代码如下:

package com.milihua.threaddemo;
import java.net.URL;
public class ThreadRunDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunner());
        //调用start方法启动线程
        thread.start();
        //获取当前classpath的绝对路径
        URL url = Thread.currentThread().getContextClassLoader().getResource("");
        System.out.println(url.toString());
        //返回线程的名称
        String threadName = thread.getName();
        System.out.println("线程名称为:" + threadName);
        //返回线程的状态
        Thread.State  state = thread.getState();
        System.out.println("线程状态为:" + state.toString());
    }
}
MyRunner类的代码如下:
package com.milihua.threaddemo;
public class MyRunner implements Runnable {
    @Override
    public void run() {
        // 在线程中执行的代码
        for( int i = 0; i<100; i++ ) {
            System.out.println("MyRunner:" + i);
        }
    }
}

程序输出结果如下图所示:

blob.png

图2  Thread方法使用示例输出结果

文章小结

一个线程从创建到消亡,会经历不同的阶段。当创建Thread类或其子类的一个实例时,就意味者该线程处于新建状态,一旦调用了该线程实例的start方法,该线程便进入了运行状态,在线程的运行过程中,它不一定能始终保持运行状态,可能会被阻塞进入等待状态、也可能会进入休眠状态,也可能会被再次唤醒进入运行状态,状态切换一直持续,直到线程消亡。

思考与练习

1、创建两个子线程,让其中一个输出1-100之间的偶数,另一个输出1-100之间的奇数。

2、模拟火车站售票窗口,开启三个窗口售票,总票为100张,当无票可售时,需要让线程进入睡眠状态,等待被唤醒。

我要评论
全部评论
郎宏林
授课老师
授课老师简介
项目经理,系统分析和架构师,从事多年中文信息处理技术。熟悉项目管理、擅长项目需求分析和设计、精通Java、C#、Python等编程语言。
下载APP

手机、电脑同步学

用微信或手机浏览器扫描二维码,即可下载APP。

  • 备案号:鲁ICP备15001146号
  • @1997-2018 潍坊米粒花网络技术有限公司版权所有