死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它。

死锁范例

ThreadDeadlock.java

packagecom.journaldev.threads;

 

public classThreadDeadlock {

 

    public static void main(String[] args)throws InterruptedException {

        Object obj1 = new Object();

        Object obj2 = new Object();

        Object obj3 = new Object();

 

        Thread t1 = new Thread(newSyncThread(obj1, obj2), "t1");

        Thread t2 = new Thread(newSyncThread(obj2, obj3), "t2");

        Thread t3 = new Thread(newSyncThread(obj3, obj1), "t3");

 

        t1.start();

        Thread.sleep(5000);

        t2.start();

        Thread.sleep(5000);

        t3.start();

 

    }

 

}

 

class SyncThreadimplements Runnable{

    private Object obj1;

    private Object obj2;

 

    public SyncThread(Object o1, Object o2){

        this.obj1=o1;

        this.obj2=o2;

    }

    @Override

    public void run() {

        String name =Thread.currentThread().getName();

        System.out.println(name + " acquiringlock on "+obj1);

        synchronized (obj1) {

         System.out.println(name + "acquired lock on "+obj1);

         work();

         System.out.println(name + "acquiring lock on "+obj2);

         synchronized (obj2) {

            System.out.println(name + "acquired lock on "+obj2);

            work();

        }

         System.out.println(name + "released lock on "+obj2);

        }

        System.out.println(name + "released lock on "+obj1);

        System.out.println(name + "finished execution.");

    }

    private void work() {

        try {

            Thread.sleep(30000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。

在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。

这些线程以向第一个对象获取封锁这种方式运行。但是当它试着像第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。

当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。

t1 acquiringlock on java.lang.Object@6d9dd520

t1 acquired lockon java.lang.Object@6d9dd520

t2 acquiringlock on java.lang.Object@22aed3a5

t2 acquired lockon java.lang.Object@22aed3a5

t3 acquiringlock on java.lang.Object@218c2661

t3 acquired lockon java.lang.Object@218c2661

t1 acquiringlock on java.lang.Object@22aed3a5

t2 acquiringlock on java.lang.Object@218c2661

t3 acquiringlock on java.lang.Object@6d9dd520

在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际生活所用的应用中,发现死锁并将它排除是非常难的。

分析死锁

为了分析一个死锁,我们需要用到命令jps,jstack,jconsole,VisualVM,或选择自己熟悉的工具,然后需要关注下应用中的Java线程转存.

jps

VisualVM

jstack

jconsole

以下就是上述程序的线程转存。

这三个线程转存的输出清楚地说明了死锁环境和线程,以及包含死锁环境的资源。

为了分析死锁,我们需要关注死锁状态的线程,然后资源再等待去封锁,每一个资源都有一个独特的ID,有了这个ID我们就能发现是哪一个进程已经封锁住对象。举个例子,线程“t3”正在等待封锁0x000000013df2f658,但是它已经被线程“t1”封锁住了。

当我们分析死锁环境的时候,如果发现线程正在引起死锁,这是我们就要改变代码来避免死锁的产生。

避免死锁

避免 死锁的方法就是不要写 顺序不一致加锁 的代码

如果 必须要写顺序不一致的加锁的代码,可以采用 trylock 方法,避免程序一直等待。

预防 死锁可以采用finddebug插件去扫描代码发现死锁。

处理 死锁,如果运行中的程序已经发生死锁,据我所知没有别的办法,只能停止程序,代价非常大,所以要尽量避免死锁。

有很多方针可供我们使用来避免死锁的局面。

避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。

public voidrun() {

    String name =Thread.currentThread().getName();

    System.out.println(name + " acquiringlock on " + obj1);

    synchronized (obj1) {

        System.out.println(name + "acquired lock on " + obj1);

        work();

    }

    System.out.println(name + " releasedlock on " + obj1);

    System.out.println(name + " acquiringlock on " + obj2);

    synchronized (obj2) {

        System.out.println(name + "acquired lock on " + obj2);

        work();

    }

    System.out.println(name + " releasedlock on " + obj2);

    System.out.println(name + " finishedexecution.");

}

只对有请求的进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。

避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值