Java程序死锁问题原理及解决方案.docx
- 文档编号:8986676
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:21
- 大小:21.98KB
Java程序死锁问题原理及解决方案.docx
《Java程序死锁问题原理及解决方案.docx》由会员分享,可在线阅读,更多相关《Java程序死锁问题原理及解决方案.docx(21页珍藏版)》请在冰豆网上搜索。
Java程序死锁问题原理及解决方案
Java程序死锁问题原理及解决方案
Java程序死锁问题原理及解决方案
死锁描述
事实上,计算机世界有很多事情需要多线程方式去解决,因为这样才能最大程度上利用资源,才能体现出计算的高效。
但是,实际上来说,计算机系统中有很多一次只能由一个进程使用的资源的情况,例如打印机,同时只能有一个进程控制它。
在多通道程序设计环境中,若干进程往往要共享这类资源,而且一个进程所需要的资源还很有可能不止一个。
因此,就会出现若干进程竞争有限资源,又推进顺序不当,从而构成无限期循环等待的局面。
我们称这种状态为死锁。
简单一点描述,死锁是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。
很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。
系统发生死锁现象不仅浪费大量的系统资源,甚至导致整个系统崩溃,带来灾难性后果。
所以,对于死锁问题在理论上和技术上都必须予以高度重视。
银行家算法
一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产。
银行家就像一个操作系统,客户就像运行的进程,银行家的资金就是系统的资源。
银行家算法需要确保以下四点:
当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客;
顾客可以分期贷款,但贷款的总数不能超过最大需求量;
当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款;
当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。
清单1.银行家算法实现
/*一共有5个进程需要请求资源,有3类资源*/
publicclassBankDemo{
//每个进程所需要的最大资源数
publicstaticintMAX[][]={{7,5,3},{3,2,2},{9,0,2},
{2,2,2},{4,3,3}};
//系统拥有的初始资源数
publicstaticintAVAILABLE[]={10,5,7};
//系统已给每个进程分配的资源数
publicstaticintALLOCATION[][]={{0,0,0},{0,0,0},{0,0,0},
{0,0,0},{0,0,0}};
//每个进程还需要的资源数
publicstaticintNEED[][]={{7,5,3},{3,2,2},{9,0,2},
{2,2,2},{4,3,3}};
//每次申请的资源数
publicstaticintRequest[]={0,0,0};
//进程数与资源数
publicstaticintM=5,N=3;
intFALSE=0;
intTRUE=1;
publicvoidshowdata(){
inti,j;
System.out.print("系统可用的资源数为:
/n");
for(j=0;j System.out.print("资源"+j+": "+AVAILABLE[j]+""); } System.out.println(); System.out.println("各进程还需要的资源量: "); for(i=0;i System.out.print("进程"+i+": "); for(j=0;j System.out.print("资源"+j+": "+NEED[i][j]+""); } System.out.print("/n"); } System.out.print("各进程已经得到的资源量: /n"); for(i=0;i System.out.print("进程"); System.out.print(i); for(j=0;j System.out.print("资源"+j+": "+ALLOCATION[i][j]+""); } System.out.print("/n"); } } //分配资源,并重新更新各种状态 publicvoidchangdata(intk){ intj; for(j=0;j AVAILABLE[j]=AVAILABLE[j]-Request[j]; ALLOCATION[k][j]=ALLOCATION[k][j]+Request[j]; NEED[k][j]=NEED[k][j]-Request[j]; } }; //回收资源,并重新更新各种状态 publicvoidrstordata(intk){ intj; for(j=0;j AVAILABLE[j]=AVAILABLE[j]+Request[j]; ALLOCATION[k][j]=ALLOCATION[k][j]-Request[j]; NEED[k][j]=NEED[k][j]+Request[j]; } }; //释放资源 publicvoidfree(intk){ for(intj=0;j AVAILABLE[j]=AVAILABLE[j]+ALLOCATION[k][j]; System.out.print("释放"+k+"号进程的"+j+"资源! /n"); } } publicintcheck0(intk){ intj,n=0; for(j=0;j if(NEED[k][j]==0) n++; } if(n==3) return1; else return0; } //检查安全性函数 //所以银行家算法其核心是: 保证银行家系统的资源数至少不小于一个客户的所需要的资源数。 在安全性检查函数chkerr()上由这个方法来实现 //这个循环来进行核心判断,从而完成了银行家算法的安全性检查工作。 publicintchkerr(ints){ intWORK; intFINISH[]=newint[M],temp[]=newint[M];//保存临时的安全进程序列 inti,j,k=0; for(i=0;i FINISH[i]=FALSE; for(j=0;j WORK=AVAILABLE[j];//第j个资源可用数 i=s; //判断第i个进程是否满足条件 while(i if(FINISH[i]==FALSE&&NEED[i][j]<=WORK){ WORK=WORK+ALLOCATION[i][j]; FINISH[i]=TRUE; temp[k]=i; k++; i=0; }else{ i++; } } for(i=0;i if(FINISH[i]==FALSE){ System.out.print("/n系统不安全! ! ! 本次资源申请不成功! /n"); return1; } } System.out.print("/n经安全性检查,系统安全,本次分配成功。 /n"); System.out.print("本次安全序列: "); for(i=0;i System.out.print("进程"+temp[i]+"->"); } System.out.print("进程"+temp[M-1]); System.out.println("/n"); return0; } } 死锁示例 死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。 在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。 死锁问题是在多线程开发中应该坚决避免和杜绝的问题。 一般来说,要出现死锁问题需要满足以下条件: 1.互斥条件: 一个资源每次只能被一个线程使用。 2.请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。 3.不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺。 4.循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。 只要破坏死锁4个必要条件之一中的任何一个,死锁问题就能被解决。 我们先来看一个示例,前面说过,死锁是两个甚至多个线程被永久阻塞时的'一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。 代码清单2所示的示例中,我们编写了一个简单的程序,它将会引起死锁发生,然后我们就会明白如何分析它。 清单2.死锁示例 publicclassThreadDeadlock{ publicstaticvoidmain(String[]args)throwsInterruptedException{ objectobj1=newobject(); objectobj2=newobject(); objectobj3=newobject(); Threadt1=newThread(newSyncThread(obj1,obj2),"t1"); Threadt2=newThread(newSyncThread(obj2,obj3),"t2"); Threadt3=newThread(newSyncThread(obj3,obj1),"t3"); t1.start(); Thread.sleep(5000); t2.start(); Thread.sleep(5000); t3.start(); } } classSyncThreadimplementsRunnable{ privateobjectobj1; privateobjectobj2; publicSyncThread(objecto1,objecto2){ this.obj1=o1; this.obj2=o2; } @Override publicvoidrun(){ Stringname=Thread.currentThread().getName(); System.out.println(name+"acquiringlockon"+obj1); synchronized(obj1){ System.out.println(name+"acquiredlockon"+obj1); work(); System.out.println(name+"acquiringlockon"+obj2); synchronized(obj2){ System.out.println(name+"acquiredlockon"+obj2); work(); } System.out.println(name+"releasedlockon"+obj2); } System.out.println(name+"releasedlockon"+obj1); System.out.println(name+"finishedexecution."); } privatevoidwork(){ try{ Thread.sleep(30000); }catch(InterruptedExceptione){ e.printStackTrace(); } } } 在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。 在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。 这些线程以向第一个对象获取封锁这种方式运行。 但是当它试着向第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。 这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。 当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。 输出如清单3所示。 清单3.清单2运行输出 t1acquiredlockonjava.lang.object@1dd3812 t2acquiringlockonjava.lang.object@c791b9 t2acquiredlockonjava.lang.object@c791b9 t3acquiringlockonjava.lang.object@1aa9f99 t3acquiredlockonjava.lang.object@1aa9f99 t1acquiringlockonjava.lang.object@c791b9 t2acquiringlockonjava.lang.object@1aa9f99 在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际所用的应用中,发现死锁并将它排除是非常难的。 死锁情况诊断 JVM提供了一些工具可以来帮助诊断死锁的发生,如下面程序清单4所示,我们实现了一个死锁,然后尝试通过jstack命令追踪、分析死锁发生。 清单4.死锁代码 importjava.util.concurrent.locks.ReentrantLock; //下面演示一个简单的死锁,两个线程分别占用south锁和north锁,并同时请求对方占用的锁,导致死锁 publicclassDeadLockextendsThread{ protectedobjectmyDirect; staticReentrantLocksouth=newReentrantLock(); staticReentrantLocknorth=newReentrantLock(); publicDeadLock(objectobj){ this.myDirect=obj; if(myDirect==south){ this.setName("south"); }else{ this.setName("north"); } } @Override publicvoidrun(){ if(myDirect==south){ try{ north.lockInterruptibly();//占用north try{ Thread.sleep(500); }catch(Exceptionex){ ex.printStackTrace(); } south.lockInterruptibly(); System.out.println("cartosouthhaspassed"); }catch(InterruptedExceptionex){ System.out.println("cartosouthiskilled"); ex.printStackTrace(); }finally{ if(north.isHeldByCurrentThread()){ north.unlock(); } if(south.isHeldByCurrentThread()){ south.unlock(); } } } if(myDirect==north){ try{ south.lockInterruptibly();//占用south try{ Thread.sleep(500); }catch(Exceptionex){ ex.printStackTrace(); } north.lockInterruptibly(); System.out.println("cartonorthhaspassed"); }catch(InterruptedExceptionex){ System.out.println("cartonorthiskilled"); ex.printStackTrace(); }finally{ if(north.isHeldByCurrentThread()){ north.unlock(); } if(south.isHeldByCurrentThread()){ south.unlock(); } } } } publicstaticvoidmain(String[]args)throwsInterruptedException{ DeadLockcar2south=newDeadLock(south); DeadLockcar2north=newDeadLock(north); car2south.start(); car2north.start(); } } jstack可用于导出Java应用程序的线程堆栈,-l选项用于打印锁的附加信息。 我们运行jstack命令,输出入清单5和6所示,其中清单5里面可以看到线程处于运行状态,代码中调用了拥有锁投票、定时锁等候和可中断锁等候等特性的ReentrantLock锁机制。 清单6直接打印出出现死锁情况,报告north和sourth两个线程互相等待资源,出现了死锁。 清单5.jstack运行输出1 [root@facenode4~]#jstack-l31274 2015-01-2912: 40: 27 FullthreaddumpJavaHotSpot(TM)64-BitServerVM(20.45-b01mixedmode): "AttachListener"daemonprio=10tid=0x00007f6d3c001000nid= 0x7a87waitingoncondition[0x0000000000000000] java.lang.Thread.State: RUNNABLE Lockedownablesynchronizers: -None "DestroyJavaVM"prio=10tid=0x00007f6da4006800nid= 0x7a2bwaitingoncondition[0x0000000000000000] java.lang.Thread.State: RUNNABLE Lockedownablesynchronizers: -None "north"prio=10tid=0x00007f6da4101800nid= 0x7a47waitingoncondition[0x00007f6d8963b000] java.lang.Thread.State: WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor<0x000000075903c7c8>( ajava.util.concurrent.locks.ReentrantLock$NonfairSync) atjava.util.concurrent.locks.LockSupport.park(LockSupport.java: 156) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java: 811) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java: 867) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java: 1201) atjava.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java: 312) atDeadLock.run(DeadLock.java: 50) Lockedownablesynchronizers: -<0x000000075903c798>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) "south"prio=10tid=0x00007f6da4100000nid= 0x7a46waitingoncondition[0x00007f6d8973c000] java.lang.Thread.State: WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor<0x000000075903c798>( ajava.util.concurrent.locks.ReentrantLock$NonfairSync) atjava.util.concurrent.locks.LockSupport.park(LockSupport.java: 156) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. parkAndCheckInterrupt(AbstractQueuedSynchronizer.java: 811) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. doAcquireInterruptibly(AbstractQueuedSynchronizer.java: 867) atjava.util.concurrent.locks.AbstractQueuedSynchronizer. acquireInterruptibly(AbstractQueuedSynchronizer.java: 1201) atjava.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java: 312) atDeadLock.run(DeadLock.java: 28) Lockedownablesynchronizers: -<0x000000075903c7c8>(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) "LowMemoryDetector"daemonprio=10tid=0x00007f6da40d2800nid= 0x7a44runnable[0x0000000000000000] java.lang.Thread.State: RUNNABLE Lockedownablesynchronizers: -None "C2CompilerThread1"daemonprio=10tid=0x00007f6da40d0000nid= 0x7a43waitingoncondition[0x0000000000000000] java.lang.Thread.State: RUNNABLE Lockedownablesynchronizers: -None "C2CompilerThread0"daemonprio=10tid=0x00007f6da40cd000nid= 0x7a42waitingoncondition[0x0000000000000000] java.lang.Thread.State: RUNNABLE Lockedownablesynchronizers: -None "SignalDispatcher"daemonprio=10tid=0x00007f6da40cb00
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 程序 死锁 问题 原理 解决方案
![提示](https://static.bdocx.com/images/bang_tan.gif)