Java之死锁编码及定位分析

目录

一、什么是死锁

二、产生死锁的主要原因

三、代码示例死锁

3.1synchornized死锁

3.2ReentrantLock死锁

四、如何定位死锁

五、死锁预防


一、什么是死锁

        两个或两个以上的进程再执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,他们都将无法推进下去。

 

二、产生死锁的主要原因

(1)系统资源不足

(2)进程运行推进的顺序不合适

(3)资源分配不当

三、代码示例死锁

  • 3.1synchornized死锁

package com.study.thread;
/**
 * synchornized死锁
 * 两个或两个以上的进程再执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,他们都将无法推进下去
 * lockA获取lockB
 * lockB获取lockA
 */
public class HoldLockThreadDemo {
    public static void main(String[] args) {
        new Thread(new LockThread("lockA", "lockB"), "AA").start();
        new Thread(new LockThread("lockB", "lockA"), "BB").start();
    }
}
class LockThread implements Runnable {
    private String lock1 = "";
    private String lock2 = "";

    public LockThread(String lock1, String lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    @Override
    public void run() {
        synchronized (lock1) {
            System.out.println(Thread.currentThread().getName() + "已获取到锁" + lock1 + ",正在获取锁" + lock2);
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + "获取到锁" + lock2);
            }
        }
    }
}
  • 3.2ReentrantLock死锁

package com.study.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 死锁
 * 两个或两个以上的进程再执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,他们都将无法推进下去
 * lockA获取lockB
 * lockB获取lockA
 */
public class HoldLockThreadDemo {
    public static void main(String[] args) {
        LockThrea2 lockThrea2 = new LockThrea2();
        new Thread(() -> {
            lockThrea2.execLock(true);
        }, "CC").start();
        new Thread(() -> {
            lockThrea2.execLock(false);
        }, "DD").start();

    }
}


class LockThrea2 {
    private Lock lock1 = new ReentrantLock();
    private Lock lock2 = new ReentrantLock();
    public void execLock(boolean flag) {
        if (flag) {
            lock1.lock();
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "获取到锁");
                lock2.lock();
                System.out.println(Thread.currentThread().getName() + "获取到锁");
                lock2.unlock();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock1.unlock();
            }
        } else {
            lock2.lock();
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "获取到锁");
                lock1.lock();
                System.out.println(Thread.currentThread().getName() + "获取到锁");
                lock1.unlock();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock2.unlock();
            }
        }

    }

}

四、如何定位死锁

(1)先通过jps查询当前运行的java程序pid号

(2)使用jstack获取线程栈,jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁死循环请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

(3)还可以使用Jconsole查看定位线程是否死锁,Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。

五、死锁预防

1、以确定的顺序获得锁,如果必须获取多个锁,那么在设计的时候需要充分考虑不同线程之前获得锁的顺序

2、当使用synchronized关键词提供的内置锁时,只要线程没有获得锁,那么就会永远等待下去,然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法,该方法可以按照固定时长等待锁,因此线程可以在获取锁超时以后,主动释放之前已经获得的所有的锁。通过这种方式,也可以很有效地避免死锁。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值