9线程的生命周期

线程声明周期

生命周期中各个节点的状态转移机制是怎样的

通用线程生命周期

  1. 初始状态
    编程语言层面的线程被创建,操作系统层面真正的线程未创建,不可分配CPU
  2. 可运行状态
    操作系统层面线程创建完成,可分配CPU
  3. 运行状态
    有空闲CPU时,分配到CPU的线程变为运行态
  4. 休眠状态
    运行态线程调用阻塞API(如阻塞方式读取文件)或等待某个事件(如条件变量),此时线程进入休眠态,释放CPU。等待事件出现后,从休眠太转变为可运行态(休眠态不会获得CPU使用权)
  5. 终止状态
    执行完毕或异常才进入终止,生命周期结束,不可切换为其他状态。

Java中线程的生命周期

  • NEW(初始化状态)
    调用线程对象的start方法即可从NEW状态转换到RUNNABLE状态
  • RUNNABLE(可运行/运行状态)
  • BLOCKED(阻塞状态)
  • WAITING(无时限等待)
  • TIMED_WAITING(有时限等待)
  • TERMINATED(终止状态)

Java线程中的BLOCKED、WAITING、TIMED_WAITING都是操作系统线程的休眠状态,没有CPU的使用权

NEW 到 RUNNABLE

调用线程对象的start方法即可从NEW状态转换到RUNNABLE状态

创建 Thread 对象主要有两种方法

  1. 继承Thread重写run方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 自定义线程对象
    class MyThread extends Thread {
    public void run() {
    // 线程需要执行的代码
    ......
    }
    }
    // 创建线程对象
    MyThread myThread = new MyThread();
  2. 实现Runable接口,重写run方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 实现 Runnable 接口
    class Runner implements Runnable {
    @Override
    public void run() {
    // 线程需要执行的代码
    ......
    }
    }
    // 创建线程对象
    Thread thread = new Thread(new Runner());

RUNNABLE 与 BLOCKED

只有一种场景会触发,线程等待synchronized的隐式锁就会从RUNNABLE转换到BLOCKED状态

线程调用阻塞式API(如阻塞方式读取文件、网络通信)
操作系统层面线程是会转换到休眠状态的,系统线程释放CPU挂起直到I/O完成
Java线程的状态会依然保持RUNNABLE状态。
JVM层面不关心操作系统调度相关的状态,JVM看来等待CPU使用权(操作系统层面此时处于可执行状态,JVM层合并了可运行、运行状态)与等待 I/O(操作系统层面此时处于休眠状态)没有区别。

我们平时所谓的Java在调用阻塞式API时,线程会阻塞,指的是操作系统线程的状态,并不是Java线程的状态。

阻塞状态不会响应中断

RUNNABLE 与 WAITING

  1. 获得synchronized隐式锁的线程,调用无参数的Object.wait()方法
  2. 调用无参数的Thread.join()方法
  3. 调用LockSupport.park()方法
    unpark线程的状态又会从WAITING状态转换到RUNNABLE

RUNNABLE 与 TIMED_WAITING

  1. 带超时参数的Thread.sleep(long millis)方法
  2. 获得synchronized隐式锁的线程,调用带超时参数的Object.wait(long timeout)方法
  3. 调用带超时参数的Thread.join(long millis)方法\
  4. 调用带超时参数的LockSupport.parkNanos(Object blocker, long deadline)方法
  5. 调用带超时参数的LockSupport.parkUntil(long deadline)方法

TIMED_WAITING和WAITING只是超时参数

RUNNABLE 到 TERMINATED

  1. 线程执行完成自动
  2. 执行run方法时抛出异常
  3. 调用interrupt方法
  4. 调用stop方法(已弃用)
1
2
3
4
5
6
7
8
9
10
11
12
Thread th = Thread.currentThread();
while(true) {
if(th.isInterrupted()) {
break;
}
// 省略业务代码无数
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();//捕获到中断异常会重置中断状态,可能会无限循环
}
}

stop与interrupt区别

stop杀死线程,不会释放锁,导致其他线程无法获取到锁。类似的方法还有suspend,resume都不建议使用

interrupt通知线程,线程有机会做一些操作,也可忽略此通知。
通知方式:异常,主动监测

异常:在线程A wait,join,sleep时线程B调用A.interrupt,以上三个方法会抛出异常
主动监测:线程调用isInterrupted方法自己检查

诊断多线程BUG

jstack命令或者Java VisualVM可视化工具导出JVM线程栈信息,包括线程的当前状态、调用栈,还包括了锁的信息

示例

  1. 查询进程id
    jsp -l 命令查看本机所有java进程pid

  2. top查看目前正在运行的进程使用系统资源情况

  3. 导出指定进程pid所有线程信息
    jstack [-F] [-l] pid > xxx.log
    -F强制导出

  4. 分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    "consumer_redirectUrl_topic_jmq206_1546013217302" daemon prio=10 tid=0x00007f1bf03f6800 nid=0x693e waiting on condition [0x00007f1b38388000]
    java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for <0x00000000f76e21a0> (a java.util.concurrent.CountDownLatch$Sync)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1033)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1326)
    at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:282)
    at com.jd.jmq.common.network.netty.ResponseFuture.await(ResponseFuture.java:133)
    at com.jd.jmq.common.network.netty.NettyTransport.sync(NettyTransport.java:241)
    at com.jd.jmq.common.network.netty.failover.FailoverNettyClient.sync(FailoverNettyClient.java:94)
    at com.jd.jmq.client.consumer.GroupConsumer.pull(GroupConsumer.java:246)
    at com.jd.jmq.client.consumer.GroupConsumer$QueueConsumer.run(GroupConsumer.java:445)
    at java.lang.Thread.run(Thread.java:745)

    Locked ownable synchronizers:
    - None

    线程名:consumer_redirectUrl_topic_jmq206_1546013217302
    线程优先级:prio=10
    java线程的identifier:tid=0x00007f1bf03f6800
    native线程的identifier:nid=0x693e
    线程的状态:waiting on condition [0x00007f1b38388000]
    java.lang.Thread.State: TIMED_WAITING (parking)
    线程栈起始地址:[0x00007f1b38388000]

根据进程id获取线程
top -H -p
将线程ID转换为16进制,在线程dump文件中搜索相关信息
例如:27840 ==> 6cc0