• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

【Java 并发编程系列3】线程状态 & 守护线程

互联网 diligentman 5天前 11次浏览
  • 学习笔记 《 深入理解 Java 虚拟机》
  • 学习笔记 《 后端架构设计》
  • 学习笔记 《 Java 基础知识进阶》
  • 学习笔记 《 Nginx 学习笔记》
  • 学习笔记 《 前端开发杂记》
  • 学习笔记 《 设计模式学习笔记》
  • 学习笔记 《 DevOps 最佳实践指南》
  • 学习笔记 《 Netty 入门与实战》
  • 学习笔记 《 高性能MYSQL》
  • 学习笔记 《 JavaEE 常用框架》
  • 学习笔记 《 Java 并发编程学习笔记》
  • 学习笔记 《 分布式系统》
  • 学习笔记 《 数据结构与算法

1、线程状态说明

Java 的线程在运行阶段会有不同的状态,在Java代码中表现为一个枚举类 Thread.State 其定义如下:

public enum State {
 // 尚未启动的线程状态,一般是创建好的线程尚未调用start() 方法局处于NEW状态
 NEW,
       
 // 线程处于可运行的状态,表示可以被JVM执行,但是也可能处于等待其他资源的状态,例如等待处理器的资源
 // JVM 概述的将可运行与运行中,统称为RUNABLE   
 RUNNABLE,
       
 // 线程处于阻塞状态,表示线程正在等待获取锁,一般是synchronized代码或者调用某个对象wait()方法
 // 暂时获取不到锁,处于其他资源释放锁的等待状态
 BLOCKED,
       
 /**
  * 线程状态处于等待状态,可能是调用以下的方法:
  *   <li>{@link Object#wait() Object.wait}</li>
  *   <li>{@link #join() Thread.join} with no timeout</li>
  *   <li>{@link LockSupport#park() LockSupport.park}</li>
  *
  * 或者是处于等待状态的线程在等待其他线程执行特定的动作,比如当前线程调用了某个对象的Object.wait()方法 
  * 该线程将会等待其他线程调用该对象的Object.notify()方法或者Object.notifyAll()方法
  * 或者调用了Thread.join()方法的线程等待其他线程的完成,即等待其他线程进入TERMINATED状态
  */
 WAITING,
       
 /**
  * 线程处于有限时间的等待,一般是调用的了包含时间的等待放方法,比如下面的方法
    {@link #sleep Thread.sleep} 休眠了一定的时间
    {@link Object#wait(long) Object.wait}
    {@link #join(long) Thread.join} with timeout
    {@link LockSupport#parkNanos LockSupport.parkNanos}
    {@link LockSupport#parkUntil LockSupport.parkUntil}
  */
 TIMED_WAITING,
       
 /**
  * 线程处于消亡状态,表示线程已经执行完成。
  */
 TERMINATED;
  

2、线程状态的切换流程

通过上文的枚举状态说明,我们了解到Java程序运行的代码状态的具体含义,线程在自身切换的生命周期中,并不是固定处于某个状态,而是随着代码的执行在不停地切换,下图概述了Java线程的状态切换流程。

【Java 并发编程系列3】线程状态 &amp; 守护线程

  • 创建新的线程,在调用start()方法之前状态为 NEW
  • 调用start()方法,线程由 NEW 进入 RUNNABLE 状态,JVM笼统的将正在运行和等待CPU执行统称为 RUNNABLE
  • 如果调用 Object.wait() 或者LockSupport.park() 方法则进入WAITING状态,当其他线程调用Object.notify()或者Obeject.notifyAll() 方法时候,线程可能会恢复为 RUNABLE , 如果设置等待超时参数,则会进入 TIMED_WAITING 状态

比如多个线程都在等待某个对象的notify()方法或者notifyAll()被调用,在恢复的时候可能就是随机选择一个等待该对象nofify()的线程恢复,其他线程继续等待,所以此处才声明有可能恢复为RUNABLE状态

  • 调用 Thread.join() 方法也会进入WAITING或者TIMED_WAITING状态,只有在对应的线程消亡后,才会跳出 WAITING 或者TIMED_WAITING 状态

  • 当程序进入同步方法或者同步代码块的时候,即进入 synchronized 关键字修饰的方法或者代码块的时候,则进入阻塞状态

一般其他的Lock同步的话,则会进入WAITING或则TIMED_WAITING 状态,而非 BLOCKED 状态

  • 程序执行完成后进入 TERINATED 状态

3、守护线程

顾名思义,守护线程就是守护其他线程的执行,当进程没有非守护进程的时候,JVM就会退出 (只有进程中存在非守护线程,VM才不会退出)。它主要用于在程序后台进行调度以及其他支持。可以通过 Thread.setDaemon(bool) 来设置当前线程是是守护线程(传参true)还是用户线程(传参false)。该设置仅在Thread处于NEW状态时候设置有效,不能在线程启动之后设置。如果设置则会抛出异常 IllegalThreadStateException

public final void setDaemon(boolean on) {
    checkAccess();
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

/**
 * Tests if this thread is alive. A thread is alive if it has
 * been started and has not yet died.
 */
public final native boolean isAlive();

当JVM退出的时候,守护线程并不一定会执行完毕,比如并不会执行finally方法,所以我们并不能依赖守护线程的finally方法来进行资源的关闭或者释放。比如下面的代码

public class DaemonThread implements Runnable {
  @Override
  public void run() {
    try {
      SleepUtils.sleep(2);
    } finally {
      // 此代码并不会被执行
      System.out.println("DaemonThread.run.finally");
    }
  }

  public static void main(String[] args) {
    Thread thread = new Thread(new DaemonThread(), "DaemonThread");
    thread.setDaemon(true);
    thread.start();
  }
}

在上一篇文章线程的创建,展示了一段线程创建的init() 方法的代码,里面的代码展示了线程的是否是守护线程会被继承到其创建的线程的,也就是说守护线程创建的线程,默认也是守护线程

this.daemon = parent.isDaemon();

喜欢 (0)