如何调试hospotVM查找死锁

说明:这篇文章并不会详细说明hospotVM查找死锁的算法,只是介绍一种如何了解hospotVM查找死锁的方法。(授人以鱼不如授人以渔)。毕竟每个人的理解层次不一样。

环境:

先来写一个死锁

public class Test {
    
    public static void main(String[] a) {
        final Object o1 = new Object();
        final Object o2 = new Object();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (o1) {
                    System.out.println("thread1获得o1");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread1尝试获得o2...");
                    synchronized (o2) {
                        System.out.println("thread1获得了o2");
                        do {
                            try {
                                Thread.sleep(5000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } while (true);
                    }
                }
            }
        }, "thread1");

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (o2) {
                    System.out.println("thread2获得o2");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("thread2尝试获得o1...");
                    synchronized (o1) {
                        System.out.println("thread2获得了o1");
                        do {
                            try {
                                Thread.sleep(5000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } while (true);
                    }
                }
            }
        }, "thread2");

        thread1.start();
        thread2.start();
        Thread.yield();
    }
}

编译好源程序后,通过自己编译的hotspotVm来运行这个死锁程序,注意需要开启JMX (-Dcom.sun.management.jmxremote)

启动程序后我们在下图的console看到程序已经进入死锁了。下面我们打开jconsole (或者jvisualVM也行)

打开jconsole并打开对应的程序,找到findDeadlockedThreads,然后点击执行 findDeadlockedThreads() 这个operation Invocation

程序进入debug模式

ThreadService::find_deadlocks_at_safepoint(_concurrent_locks)这行代码就是hotspotVM查找死锁的算法读者可以自己debug看看。

有两点需要提出来:如下图所示

1:如果发生死锁那么并然会产生竞争因此锁的级别肯定是heavyweight (不是偏向锁,轻量级锁)。所以在查找死锁的时候我们只需要关心heavyweight monitor。

2:检测死锁由两部分构成一种是ObjectMonitor管理的锁(synchronized修饰),另外一种是current包实现的锁。

-------------------------------------------------------------------

最后补充一点,如何避免死锁?

两种解决方案:

1是锁粗化,将锁1和锁2合并为1个锁。

2是锁细化但是要顺序加锁,所有要获取锁1和锁2的线程(或者内核进程)即要获得锁2必须先获得锁1。

至于这两种方案哪种好这取决于你的系统需求。第一种锁粗化简单并且只维护一个锁减少了系统开销,但是系统的扩展性不好(这里的扩展性是指在多处理器的系统架构中,锁的粒度越小多处理器串行执行临界代码的时间越小,增加处理器数量系统性能扩展性越好)。第二种锁细化扩展性好但是系统需要多一个锁增加了系统的开销。第一种适合处理器数量少,竞争不大的共享资源,第二种适合处理器数量多并且高度竞争的共享资源。一个高度竞争的Hash数据结构就比较适合第二种方式。

我们可以简单看看jdk中关于锁粗化(Collections.synchronizedMap())和锁细化(ConcurrentHashMap)的put方法是如何实现的。

Collections.synchronizedMap()

这个数据结构比较简单就是为HashMap的每个方法加了一个synchronized同步语句。如果你需要一个竞争不是很频繁的hashmap数据结构建议使用这个数据结构。

 

ConcurrentHashMap:

251行如果桶位为null则通过cas方式put node,如果不为null 则对桶位node进行synchronized同步。避免了对整个hashMap进行同步。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值