9线程的生命周期
线程声明周期
生命周期中各个节点的状态转移机制是怎样的
通用线程生命周期
- 初始状态
编程语言层面的线程被创建,操作系统层面真正的线程未创建,不可分配CPU - 可运行状态
操作系统层面线程创建完成,可分配CPU - 运行状态
有空闲CPU时,分配到CPU的线程变为运行态 - 休眠状态
运行态线程调用阻塞API(如阻塞方式读取文件)或等待某个事件(如条件变量),此时线程进入休眠态,释放CPU。等待事件出现后,从休眠太转变为可运行态(休眠态不会获得CPU使用权) - 终止状态
执行完毕或异常才进入终止,生命周期结束,不可切换为其他状态。
Java中线程的生命周期
- NEW(初始化状态)
调用线程对象的start方法即可从NEW状态转换到RUNNABLE状态 - RUNNABLE(可运行/运行状态)
- BLOCKED(阻塞状态)
- WAITING(无时限等待)
- TIMED_WAITING(有时限等待)
- TERMINATED(终止状态)
Java线程中的BLOCKED、WAITING、TIMED_WAITING都是操作系统线程的休眠状态,没有CPU的使用权
NEW 到 RUNNABLE
调用线程对象的start方法即可从NEW状态转换到RUNNABLE状态
创建 Thread 对象主要有两种方法
- 继承Thread重写run方法
1
2
3
4
5
6
7
8
9// 自定义线程对象
class MyThread extends Thread {
public void run() {
// 线程需要执行的代码
......
}
}
// 创建线程对象
MyThread myThread = new MyThread(); - 实现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
- 获得synchronized隐式锁的线程,调用无参数的Object.wait()方法
- 调用无参数的Thread.join()方法
- 调用LockSupport.park()方法
unpark线程的状态又会从WAITING状态转换到RUNNABLE
RUNNABLE 与 TIMED_WAITING
- 带超时参数的Thread.sleep(long millis)方法
- 获得synchronized隐式锁的线程,调用带超时参数的Object.wait(long timeout)方法
- 调用带超时参数的Thread.join(long millis)方法\
- 调用带超时参数的LockSupport.parkNanos(Object blocker, long deadline)方法
- 调用带超时参数的LockSupport.parkUntil(long deadline)方法
TIMED_WAITING和WAITING只是超时参数
RUNNABLE 到 TERMINATED
- 线程执行完成自动
- 执行run方法时抛出异常
- 调用interrupt方法
- 调用stop方法(已弃用)
1 | Thread th = Thread.currentThread(); |
stop与interrupt区别
stop杀死线程,不会释放锁,导致其他线程无法获取到锁。类似的方法还有suspend,resume都不建议使用
interrupt通知线程,线程有机会做一些操作,也可忽略此通知。
通知方式:异常,主动监测
异常:在线程A wait,join,sleep时线程B调用A.interrupt,以上三个方法会抛出异常
主动监测:线程调用isInterrupted方法自己检查
诊断多线程BUG
jstack命令或者Java VisualVM可视化工具导出JVM线程栈信息,包括线程的当前状态、调用栈,还包括了锁的信息
示例
查询进程id
jsp -l 命令查看本机所有java进程pidtop查看目前正在运行的进程使用系统资源情况
导出指定进程pid所有线程信息
jstack [-F] [-l] pid > xxx.log
-F强制导出分析
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