线程活跃性 是指代码有限,但由于某种原因,导致线程一直未执行完成。
1、死锁
指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
例如:
t1线程获得了lock1对象锁,接下来想获取lock2对象锁。
t2线程获得了lock2对象锁 ,接下来想获取lock1对象锁。
示例代码:
@Slf4j
public class DeadLock {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
while (true) {
synchronized (lock1) {
log.debug("t1线程获得lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
log.debug("t1线程获得lock2");
}
}
}
}, "t1").start();
new Thread(() -> {
synchronized (lock2) {
log.debug("t2线程获得lock2");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
log.debug("t2线程获得lock1");
}
}
}, "t2").start();
}
}
以上代码就会造成死锁,导致t1、t2线程都在等待,无法结束,运行结果如下
1.1、定位死锁
详细的死锁检测方式:4个java死锁检测工具:jstack、jconsole、jvisualvm、jmc - 知乎,以下先列出两种方式。
1.1.1、方式一:使用jconsole工具
JConsole 是一个Java内置性能分析器,使用详见:JConsole详解 。
jconsole 还可以查看堆内存、CPU、线程数 等其他信息。
点击检测死锁,即可以查看存在的死锁线程信息。
t1线程信息:会显示阻塞的锁对象,以及持有该锁的线程。同时会显示死锁的代码位置。
t2线程信息:会显示阻塞的锁对象,以及持有该锁的线程。同时会显示死锁的代码位置。
1.1.2、方式二:使用jps定位进程id,再用jstack定位死锁
jps命令:列出所有的java进程。
jstack:打印指定Java进程、核心文件或远程调试服务器的Java线程的Java堆栈跟踪信息。
可以生成JVM当前时刻的线程快照。线程快照是当前JVM内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
jstack命令 在最后会打印死锁信息。即可定位到死锁的类和行数。
2、活锁
多个线程之间,在互相改变对方的执行条件,导致谁都没有办法结束。
3、饥饿
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。
Java中导致饥饿的原因:
- 高优先级线程吞噬所有的低优先级线程的CPU时间。
- 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。
- 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法),因为其他线程总是被持续地获得唤醒。