说明:这篇文章并不会详细说明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进行同步。