java程序报502_Java程序的死锁

什么情况下Java程序会产生死锁?

循环依赖,彼此一直处于等待状态,并且互相都没有进展。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样 也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。

d0a8a547d226d626b1af9fecaa839227.png

如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身问题。所以,代码开发阶段互相审查,或者利用工具进行预防性排查,往往也是很重要的。

举例分析

package Test;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;public classDeadThread extends Thread{privateObject obj1;privateObject obj2;private intorder;public DeadThread(intorder, Object obj1, Object obj2) {this.order =order;this.obj1 =obj1;this.obj2 =obj2;

}public voidtest1() throws InterruptedException {

synchronized (obj1) {//建议线程调取器切换到其它线程运行

Thread.yield();

synchronized (obj2) {

System.out.println("test。。。");

}

}

}public voidtest2() throws InterruptedException {

synchronized (obj2) {

Thread.yield();

synchronized (obj1) {

System.out.println("test。。。");

}

}

}

@Overridepublic voidrun() {while (true) {try{if(this.order == 1){this.test1();

}else{this.test2();

}

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}classTest {public static voidmain(String[] args) throws InterruptedException {

Object obj1= newObject();

Object obj2= newObject();

ExecutorService ex= Executors.newFixedThreadPool(10);//起10个线程

for (int i = 0; i < 10; i++) {int order = i%2==0 ? 1 : 0;

ex.execute(newDeadThread(order, obj1, obj2));

}

}

}

这段就是典型的死锁代码,我们如何通过工具而不是人眼识别?  jstack,还有一些图形化工具,这里只讲前者

用jstack发现死锁

首先jps,这个命令就是显示java的进程pid

afa97098f81ec87a4066d778f70f895c.png

这是没有运行上面死锁代码的,我们运行一下再jps

b343fdfcd76dec318375bd552389f658.png

可以看出相关pid为11100,然后我们再用jstack分析

de2e8c0b9a321ddb0e0879d5187a398d.png

可以看出分析的结果,然后我们就可以从相关位置处查看分析了。

分析的思路大概就是

区分线程状态 -> 查看等待目标 -> 对比Monitor等持有状态

用ThreadMXBean来发现死锁

import java.lang.management.ManagementFactory;

import java.lang.management.ThreadInfo;

import java.lang.management.ThreadMXBean;public classDeadLockTesting {public static voidmain(String args[]) {

Object obj1= newObject();

Object obj2= newObject();

DeadLockThread t1= newDeadLockThread(obj1, obj2);

DeadLockThread t2= newDeadLockThread(obj2, obj1);

t1.start();

t2.start();try{

Thread.sleep(2000);

}catch(Exception e) {

e.printStackTrace();

}

MonitorThread mt= newMonitorThread();

mt.start();

}

}classMonitorThread extends Thread {public voidrun() {

ThreadMXBean tmx=ManagementFactory.getThreadMXBean();long[] ids =tmx.findDeadlockedThreads();if (ids != null) {

ThreadInfo[] infos= tmx.getThreadInfo(ids, true, true);

System.out.println("The following threads are deadlocked:");for(ThreadInfo ti : infos) {

System.out.println(ti);

}

}

}

}classDeadLockThread extends Thread {privateObject obj1;privateObject obj2;publicDeadLockThread(Object obj1, Object obj2) {this.obj1 =obj1;this.obj2 =obj2;

}public voidrun() {

synchronized (obj1) {try{

Thread.sleep(1000);

synchronized (obj2) {

}

}catch(Exception e) {

e.printStackTrace();

}

}

}

}

如何在编程中尽量预防死锁呢?

互斥条件,类似Java中Monitor都是独占的,要么是我用,要么是你用。

互斥条件是长期持有的,在使用结束之前,自己不会释放,也不能被其他线程抢占。

循环依赖关系,两个或者多个个体之间出现了锁的链条环。

第一种方法

如果可能的话,尽量避免使用多个锁,并且只有需要时才持有锁。

第二种方法

如果必须使用多个锁,尽量设计好锁的获取顺序,这个说起来简单,做起来可不容易,你可以参看著名的银行家算法。

银行家算法

8757734211133876d1d7ca9e3d50e655.png

将对象(方法)和锁之间的关系,用图形化的方式表示分别抽取出来,以今天最初讲的死锁为例,因为是调用了同一个线程所以更加简单。

0d9c4b8dd0f9b158ec5a01085438b52f.png

3c24feccbe435fe7d40622edc899c53f.png      

9fb51e2ea39670f0c4ddc1f930453138.png

很清晰就发现这有死锁的风险

第三种方法

使用带超时的方法,为程序带来更多可控性。

类似Object.wait(…)或者CountDownLatch.await(…),都支持所谓的timed_wait,我们完全可以就不假定该锁一定会获得,指定超时时间,并为无法得到锁时准备退出逻辑。

if (lock.tryLock() || lock.tryLock(timeout, unit)) { //... }

第四种方法

业界也有一些其他方面的尝试,比如通过静态代码分析(如FindBugs)去查找固定的模式,进而定位可能的死锁或者竞争情况。

findbugs是一个好用的插件,可以自行搜索使用

技巧

比如Linux上,可以使用top命令配合grep Java之类,找到忙的pid;然后,转换成16进制,就是jstack输出中的格式;再定位代码

任务线程规范命名,详细记录逻辑运行日志。jstack查看线程状态。

参考杨晓峰老师的《java核心技术》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值