线程状态

1、守护线程

  • 默认情况下,Java进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Slf4j
    public class TestThread {
    public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
    while (true) {
    if (Thread.currentThread().isInterrupted()) {
    break;
    }
    }
    log.debug("结束");
    }, "t1");
    // 设置该线程为守护线程
    t1.setDaemon(true);
    t1.start();

    Thread.sleep(1000);
    log.debug("结束");
    }
    }

由于t1线程设置为守护线程,所以当t1线程启动一秒后当非守护线程的主线程结束时,守护线程t1也会被强制结束。运行结果:

1
11:03:51.415 org.example.TestThread [main] - 结束
  • 举例:

    • 垃圾回收器线程就是一种守护线程。
    • Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接收到shutdown命令后,不会等待它们处理完当前请求。

2、操作系统层面线程的五种状态

  • 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联。
  • 【可运行状态】(就绪状态) 指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行。
  • 【运行状态】指获取了CPU时间片运行中的状态。
    • 当CPU时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换。
  • 【阻塞状态】
    • 如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用到CPU,会导致线程上下文切换,进入
      【阻塞状态】。
    • 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】。
    • 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑
      调度它们。
  • 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态。

3、Java线程的六种状态

  • 根据Thread.State枚举,分为六种状态:

    • 【NEW】 线程刚被创建,但是还没有调用start()方法。
    • 【RUNNABLE】 当调用了start()方法之后,注意,Java API层面的RUNNABLE状态涵盖了操作系统层面的【可运行状态】、【运行状态】和【阻塞状态】(由于BIO导致的线程阻塞,在Java里无法区分,仍然认为是可运行)。
    • 【BLOCKED】,【WAITING】,【TIMED_WAITING】都是Java API层面对【阻塞状态】的细分。
    • 【TERMINATED】当线程代码运行结束。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    @Slf4j
    public class TestThread {
    public static void main(String[] args) throws IOException {
    Thread t1 = new Thread("t1") {
    @Override
    public void run() {
    log.debug("running...");
    }
    };

    Thread t2 = new Thread("t2") {
    @Override
    public void run() {
    // 或者进行IO操作
    while(true) {

    }
    }
    };
    t2.start();

    Thread t3 = new Thread("t3") {
    @Override
    public void run() {
    log.debug("running...");
    }
    };
    t3.start();

    Thread t4 = new Thread("t4") {
    @Override
    public void run() {
    synchronized (TestThread.class) {
    try {
    Thread.sleep(1000000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    };
    t4.start();

    Thread t5 = new Thread("t5") {
    @Override
    public void run() {
    try {
    t2.join();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    };
    t5.start();

    Thread t6 = new Thread("t6") {
    @Override
    public void run() {
    // 由于t4线程先拿到锁而一直没有释放,导致t6线程处于blocked状态
    synchronized (TestThread.class) {
    try {
    Thread.sleep(1000000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    };
    t6.start();

    try {
    Thread.sleep(500);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    log.debug("t1 state {}", t1.getState());
    log.debug("t2 state {}", t2.getState());
    log.debug("t3 state {}", t3.getState());
    log.debug("t4 state {}", t4.getState());
    log.debug("t5 state {}", t5.getState());
    log.debug("t6 state {}", t6.getState());
    }
    }

    运行结果:

    1
    2
    3
    4
    5
    6
    7
    11:31:13.766 org.example.TestThread [t3] - running...
    11:31:14.268 org.example.TestThread [main] - t1 state NEW
    11:31:14.268 org.example.TestThread [main] - t2 state RUNNABLE
    11:31:14.268 org.example.TestThread [main] - t3 state TERMINATED
    11:31:14.268 org.example.TestThread [main] - t4 state TIMED_WAITING
    11:31:14.268 org.example.TestThread [main] - t5 state WAITING
    11:31:14.268 org.example.TestThread [main] - t6 state BLOCKED

4、线程状态的转换

  • 假设有线程Thread t。
    • 情况1:NEW–>RUNNABLE
      • 当调用t.start()方法时,由NEW–>RUNNABLE。
    • 情况2:RUNNABLE<–>WAITING
      • t线程用synchronized(obj)获取了对象锁后。
        • 调用obj.wait()方法时,t线程从RUNNABLE–>WAITING。
        • 调用obj.notify(),obj.notifyAll(),t.interrupt()时:
          • 竞争锁成功,t线程从WAITING–>RUNNABLE。
          • 竞争锁失败,t 线程从 WAITING–>BLOCKED。
    • 情况3:RUNNABLE<–>WAITING
      • 当前线程调用t.join()方法时,当前线程从RUNNABLE–>WAITING。
        • 注意是当前线程在t线程对象的监视器上等待。
      • t线程运行结束,或调用了当前线程的interrupt()时,当前线程从WAITING–>RUNNABLE。
    • 情况4:RUNNABLE<–>WAITING
      • 当前线程调用LockSupport.park()方法会让当前线程从RUNNABLE–>WAITING。
      • 调用LockSupport.unpark(目标线程)或调用了线程的interrupt(),会让目标线程从WAITING–> RUNNABLE。
    • 情况5:RUNNABLE<–>TIMED_WAITING
      • t 线程用synchronized(obj)获取了对象锁后:
        • 调用obj.wait(long n)方法时,t线程从 RUNNABLE–>TIMED_WAITING。
        • t线程等待时间超过了n毫秒,或调用obj.notify(),obj.notifyAll(),t.interrupt()时:
          • 竞争锁成功,t线程从TIMED_WAITING–>RUNNABLE。
          • 竞争锁失败,t线程从TIMED_WAITING–>BLOCKED。
    • 情况6:RUNNABLE<–>TIMED_WAITING
      • 当前线程调用t.join(long n)方法时,当前线程从RUNNABLE–>TIMED_WAITING。
        • 注意是当前线程在t线程对象的监视器上等待。
      • 当前线程等待时间超过了n毫秒,或t线程运行结束,或调用了当前线程的interrupt()时,当前线程从TIMED_WAITING–>RUNNABLE。
    • 情况7:RUNNABLE<–>TIMED_WAITING
      • 当前线程调用Thread.sleep(long n),当前线程从RUNNABLE–>TIMED_WAITING。
      • 当前线程等待时间超过了n毫秒,当前线程从TIMED_WAITING–>RUNNABLE。
    • 情况8:RUNNABLE<–>TIMED_WAITING
      • 当前线程调用LockSupport.parkNanos(long nanos)或LockSupport.parkUntil(long millis)时,当前线程从RUNNABLE–>TIMED_WAITING。
      • 调用LockSupport.unpark(目标线程)或调用了线程的interrupt(),或是等待超时,会让目标线程从TIMED_WAITING–> RUNNABLE。
    • 情况9:RUNNABLE<–>BLOCKED
      • t线程用synchronized(obj)获取了对象锁时如果竞争失败,从 RUNNABLE–>BLOCKED。
      • 持obj锁线程的同步代码块执行完毕,会唤醒该对象上所有BLOCKED的线程重新竞争,如果其中t线程竞争成功,从BLOCKED–>RUNNABLE ,其它失败的线程仍然BLOCKED。
    • 情况10:RUNNABLE<–>TERMINATED
      • 当前线程所有代码运行完毕,进入TERMINATED。