Java解决线程死锁三种方案

什么是死锁?

死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞状态。

下面代码展示一下死锁

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)

如下图:
Java解决线程死锁三种方案_第1张图片
可以看到Threa2正在处于BLOCKED的状态,而Thread1也是同样处于BLOCKED这就说明了发生了死锁。并且可以定位问题到在是在DeadLockSimple.java:24这个文件的第24行,怎么样很神奇吧。

下面我们看一下使用jConsole如何检测运行的代码是否有死锁

  • 同样的操作步骤 运行程序
  • 在JAVA_HOME/bin 目录下 运行jConsole 选择到对应的PID 可以看到对应的类名。

如下图
Java解决线程死锁三种方案_第2张图片
然后我们选择死锁就能看到了当前死锁的线程
Java解决线程死锁三种方案_第3张图片

还有重头戏也就是最牛的 在代码中就可以进行检测,定位死锁,使用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();
    }

}

你可能感兴趣的:(Java)