什么是死锁?
死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞状态。
下面代码展示一下死锁
package com.zhr.Thread;
/**
* @ Author :zhenghaoran.
* @ Date :Created in 14:55 2018/11/30
* @ Description:
*/
public class DeadLockSample extends Thread{
private String first;
private String second;
public DeadLockSample (String name,String first,String second){
super(name);
this.first = first;
this.second = second;
}
public void run(){
synchronized (first){
System.out.println(this.getName() + " obtained:" + first);
try {
Thread.sleep(1000L);
synchronized (second){
System.out.println(this.getName() +" obtained: " + second);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
String lockA = "lockA";
String lockB = "lockB";
DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
结果:
Thread1 obtained:lockA
Thread2 obtained:lockB
或者
Thread2 obtained:lockB
Thread1 obtained:lockA
看了上面的代码大家可能会有疑问在代码中明明是先启动的Thread1线程啊,为什么还会有第二个结果变成了Threa2先运行,其实这是在操作系统层面中系统会进行线程调度,在Java中线程调度是抢占式的,主线程虽然先运行了Thread1.start(),但是并不代表就一定会先运行run()方法,在两个线程没有设置线程优先级的时候默认都是5级,现在分别启动线程1核线程2,此时操作系统会将处在就绪状态的变为运行运行状态,所以这个时候线程2要是先就绪,并且cpu从就绪的线程当中选择了线程2name就会线程2先执行。
当然在线上如果写出了死锁我们应该怎么办呢?别急让我来给你介绍两个工具都是 JDK自带的 jstack 和 jConsole。下面演示jstack的用法
- 首先运行刚才的程序,让其处于死锁的状态
- 运行ps -ef | grep java 找到正在运行的java程序的pid
- 然后进入到JAVA_HOME/bin下运行 ./jstack 10586(这个是我当前运行的pid)
如下图:
可以看到Threa2正在处于BLOCKED的状态,而Thread1也是同样处于BLOCKED这就说明了发生了死锁。并且可以定位问题到在是在DeadLockSimple.java:24这个文件的第24行,怎么样很神奇吧。
下面我们看一下使用jConsole如何检测运行的代码是否有死锁
- 同样的操作步骤 运行程序
- 在JAVA_HOME/bin 目录下 运行jConsole 选择到对应的PID 可以看到对应的类名。
如下图
然后我们选择死锁就能看到了当前死锁的线程
还有重头戏也就是最牛的 在代码中就可以进行检测,定位死锁,使用Java提供的标准管理 API,ThreadMXBean 类,具体代码如下
package com.zhr.Thread;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @ Author :zhenghaoran.
* @ Date :Created in 14:55 2018/11/30
* @ Description:
*/
public class DeadLockSample extends Thread{
private String first;
private String second;
public DeadLockSample (String name,String first,String second){
super(name);
this.first = first;
this.second = second;
}
public void run(){
synchronized (first){
System.out.println(this.getName() + " obtained:" + first);
try {
Thread.sleep(1000L);
synchronized (second){
System.out.println(this.getName() +" obtained: " + second);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
Runnable dlCheck = new Runnable() {
public void run() {
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if(deadlockedThreads != null){
ThreadInfo[] threadInfos= threadMXBean.getThreadInfo(deadlockedThreads);
System.out.println("Detected deadlock threads:");
for (ThreadInfo threadInfo : threadInfos) {
System.out.println(threadInfo.getThreadName());
}
}
}
};
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
//稍等 1 秒,然后每 2 秒进行一次死锁扫描
scheduler.scheduleAtFixedRate(dlCheck,1L,2L, TimeUnit.SECONDS);
String lockA = "lockA";
String lockB = "lockB";
DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
t1.start();
t2.start();
t1.join();
t2.join();
}
}