并发死锁

JVM可以检测到synchronized导致的死锁,考虑下面的代码:

public class ClassA {
    public static String monitor1 = "monitor1";
    public static String monitor2 = "monitor2";
    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                try {
                    synchronized (monitor1) {
                        System.out.println(Thread.currentThread().getName() + ": got monitor1");
                        Thread.sleep(5000);
                        synchronized (monitor2) {
                            System.out.println(Thread.currentThread().getName() + ": got monitor2");
                        }
                    }
                } catch (InterruptedException e) {}
            }
        }, "I'm Thread1");
        thread1.start();
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                try {
                    synchronized (monitor2) {
                        System.out.println(Thread.currentThread().getName() + ": got monitor2");
                        Thread.sleep(5000);
                        synchronized (monitor1) {
                            System.out.println(Thread.currentThread().getName() + ": got monitor1");
                        }
                    }
                } catch (InterruptedException e) {}
            }
        }, "I'm Thread2");
        thread2.start();
    }
}


代码中有意制造了典型的死锁情况,即在持有资源的情况下请求新的资源,并且两个线程交叉请求资源,从而导致在特定情况下发生死锁。

在JVM监测中可以明显看到死锁并可以准确定位造成死锁的代码,如下图:

image

继续进行Thread Dump,在Thread Dump中发现错误代码的位置,如下图:

其中明确说明了造成死锁的原因。

上面描述的死锁是有意制造的简单场景,实际系统中的情况通常复杂得多,因此在开发过程中synchronized的使用应该谨慎,因为死锁很容易监测出来,但无法在JVM运行过程中简单地处理死锁。

Java开发过程中,无论是使用synchronized关键字,还是使用其他锁定机制(如java.util.concurrent.locks.*)中的方法,在编程错误的情况下都可能出现死锁。死锁是操作系统课程中的重要内容,在多线程环境中编程的程序员应该了解其概念及如何避免死锁。

附:死锁产生的必要条件

如果在系统中同时具备下面四个必要条件时,那么可能发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。

  • 互斥条件:即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如打印机就是一种独占资源,两份文档不能同时打印;
  • 不可抢占条件:进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放;
  • 占有且申请条件:进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源;
  • 循环等待条件:存在一个进程等待序列{P1,P2,...,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,......,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环;

上面四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。

posted @ 2016-03-07 10:44  熊猫太郎  阅读(502)  评论(0编辑  收藏  举报