Java线程生命周期和运行流程中的状态切换

概念

线程生命周期是描述了线程从创建到死亡这一阶段的所有状态,出于系统的调度机制不同以及我们需要更灵活的编写更加高效的线程执行逻辑的目的,在线程生命周期每个节点状态下,Java为我们提供了一套方法,掌握线程生命周期状态和控制这一状态的方法,可以帮我们编写强壮的多线程代码。

生命周期

1. 初始化状态

当我们新创建一个线程的时候,意味着线程已经进入初始化状态

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
      //do something
    }
});

2. 就绪状态

当我们调用线程的start()方法的时候,该线程处于就绪状态,暂时不会立即执行run方法中的任务,因为会等待CPU分配资源调度执行

thread.start();

3. 运行状态

当CPU开始调度执行已存在的就绪状态的线程时候,线程真正开始运行,此时会执行 run()方法的任务

4. 阻塞状态

由于CPU调度机制或人为的干预,线程在某些时候会处于阻塞状态,阻塞状态分为以下几种状况:

  1. 线程调用sleep()方法,主动放弃所占用的处理器资源,暂时进入中断状态(不会释放持有的对象锁),超时后等待系统分配CPU继续执行;
  2. 线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;
  3. 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;
  4. 线程调用wait方法,等待notify/notifyAll唤醒时(会释放持有的对象锁);

阻塞状态分类:

  1. 等待阻塞:运行状态中的 线程执行wait()方法,或者LockSupport.park(),使本线程进入到等待阻塞状态;
  2. 同步阻塞:线程在 获取synchronized同步锁失败(因为锁被其它线程占用),它会进入到同步阻塞状态;
  3. 超时阻塞:通过调用线程的 sleep()或join()或发出I/O请求 时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕 时,线程重新转入就绪状态;

在阻塞状态的线程只能进入就绪状态,无法直接进入运行状态。而就绪和运行状态之间的转换通常不受程序控制,而是由系统线程调度所决定。当处于就绪状态的线程获得处理器资源时,该线程进入运行状态;当处于运行状态的线程失去处理器资源时,该线程进入就绪状态。一个例外方法,调用yield()方法可以让运行状态的线程转入就绪状态

5. 死亡终止状态

线程终止会以如下三种方式结束,结束后就处于 死亡状态

  1. run()方法执行完成,线程正常结束;推荐使用
  2. 线程抛出一个未捕获的异常Exception或Error
  3. 直接调用该线程stop()方法来结束该线程—该方法容易导致死锁,不推荐使用;
  4. 使用线程interrupt中断标志,来礼貌性的通知线程结束

线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常

状态流程图

java生命周期状态切换图

线程方法

public class Thread{
    // 线程的启动
    public void start(); 
    // 线程体
    public void run(); 
    // 已废弃
    public void stop(); 
    // 已废弃
    public void resume(); 
    // 已废弃
    public void suspend(); 
    // 在指定的毫秒数内让当前正在执行的线程休眠
    public static void sleep(long millis); 
    // 同上,增加了纳秒参数
    public static void sleep(long millis, int nanos); 
    // 测试线程是否处于活动状态
    public boolean isAlive(); 
    // 中断线程
    public void interrupt(); 
    // 测试线程是否已经中断
    public boolean isInterrupted(); 
    // 测试当前线程是否已经中断
    public static boolean interrupted(); 
    // 等待该线程终止
    public void join() throws InterruptedException; 
    // 等待该线程终止的时间最长为 millis 毫秒
    public void join(long millis) throws InterruptedException; 
    // 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
    public void join(long millis, int nanos) throws InterruptedException; 
}

线程经典问题

Thread 对象上 的 run()方法和start()方法有什么区别?

start()run()
创建一个新线程,并在新创建的线程上执行run()方法有创建新线程,并且在调用线程本身上执行run()方法
不能被多次调用,否则抛出java.lang.IllegalStateException可以多次调用
java.lang.Thread类中定义。java.lang.Runnable接口中定义,并且必须在实现类中重写

关于线程中 sleep() await/wait() yield() 方法的区别

---await / waitsleepyield
是否释放锁释放不释放不释放
何时恢复就绪状态notify唤醒后超时后立即进入就绪状态
谁的方法Condition / ObjectThreadThread
执行代码块同步代码块任意位置任意位置

本套教程是一套全面覆盖JAVA多线程的教程,其他章节可登陆网站查看
涵盖内容:包括但是不限制,线程基础理论,线程安全,锁,调度机制,线程通信,并发容器,并发框架,并发工具类,线程池,线程性能测试与问题定位

  JVM线程调度机制
多线程开发之前你必须了解的基本概念 
上一篇: JVM线程调度机制
下一篇:多线程开发之前你必须了解的基本概念
评论

如果我的文章对你有帮助,或许可以打赏一下呀!

支付宝
微信
QQ