




已阅读5页,还剩25页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Java程序死锁问题原理及解决方案 Java语言通过synchronized关键字来保证原子性,这是因为每一个object都有一个隐含的锁,这个也称作监视器对象。在进入synchronized之前自动获取此内部锁,而一旦离开此方式,无论是完成或者中断都会自动释放锁。显然这是一个独占锁,每个锁请求之间是互斥的。下面是为大家带来的Java程序死锁问题原理及解决方案,欢迎阅读。 死锁描述 死锁是操作系统层面的一个错误,是进程死锁的简称,最早在1965年由Dijkstra在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。 事实上,计算机世界有很多事情需要多线程方式去解决,因为这样才能最大程度上利用资源,才能体现出计算的高效。但是,实际上来说,计算机系统中有很多一次只能由一个进程使用的资源的情况,例如打印机,同时只能有一个进程控制它。在多通道程序设计环境中,若干进程往往要共享这类资源,而且一个进程所需要的资源还很有可能不止一个。因此,就会出现若干进程竞争有限资源,又推进顺序不当,从而构成无限期循环等待的局面。我们称这种状态为死锁。简单一点描述,死锁是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。很显然,如果没有外力的作用,那么死锁涉及到的各个进程都将永远处于封锁状态。 系统发生死锁现象不仅浪费大量的系统资源,甚至导致整个系统崩溃,带来灾难性后果。所以,对于死锁问题在理论上和技术上都必须予以高度重视。 银行家算法 一个银行家如何将一定数目的资金安全地借给若干个客户,使这些客户既能借到钱完成要干的事,同时银行家又能收回全部资金而不至于破产。银行家就像一个操作系统,客户就像运行的进程,银行家的资金就是系统的资源。 银行家算法需要确保以下四点: 当一个顾客对资金的最大需求量不超过银行家现有的资金时就可接纳该顾客; 顾客可以分期贷款,但贷款的总数不能超过最大需求量; 当银行家现有的资金不能满足顾客尚需的贷款数额时,对顾客的贷款可推迟支付,但总能使顾客在有限的时间里得到贷款; 当顾客得到所需的全部资金后,一定能在有限的时间里归还所有的资金。 清单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+:+AVAILABLEj+); System.out.println(); System.out.println(各进程还需要的资源量:); for(i=0;i System.out.print(进程+i+:); for(j=0;j System.out.print(资源+j+:+NEEDij+); 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+:+ALLOCATIONij+); System.out.print(/n); /分配资源,并重新更新各种状态 publicvoidchangdata(intk) intj; for(j=0;j AVAILABLEj=AVAILABLEj-Requestj; ALLOCATIONkj=ALLOCATIONkj+Requestj; NEEDkj=NEEDkj-Requestj; ; /回收资源,并重新更新各种状态 publicvoidrstordata(intk) intj; for(j=0;j AVAILABLEj=AVAILABLEj+Requestj; ALLOCATIONkj=ALLOCATIONkj-Requestj; NEEDkj=NEEDkj+Requestj; ; /释放资源 publicvoidfree(intk) for(intj=0;j AVAILABLEj=AVAILABLEj+ALLOCATIONkj; System.out.print(释放+k+号进程的+j+资源!/n); publicintcheck0(intk) intj,n=0; for(j=0;j if(NEEDkj=0) n+; if(n=3) return1; else return0; /检查安全性函数 /所以银行家算法其核心是:保证银行家系统的资源数至少不小于一个客户的所需要的资源数。在安全性检查函数chkerr()上由这个方法来实现 /这个循环来进行核心判断,从而完成了银行家算法的安全性检查工作。 publicintchkerr(ints) intWORK; intFINISH=newintM,temp=newintM;/保存临时的安全进程序列 inti,j,k=0; for(i=0;i FINISHi=FALSE; for(j=0;j WORK=AVAILABLEj;/第j个资源可用数 i=s; /判断第i个进程是否满足条件 while(i if(FINISHi=FALSE&NEEDij); System.out.print(进程+tempM-1); System.out.println(/n); return0; 死锁示例 死锁问题是多线程特有的问题,它可以被认为是线程间切换消耗系统性能的一种极端情况。在死锁时,线程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是系统任务永远无法执行完成。死锁问题是在多线程开发中应该坚决避免和杜绝的问题。 一般来说,要出现死锁问题需要满足以下条件: 1.互斥条件:一个资源每次只能被一个线程使用。 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。 4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 只要破坏死锁4个必要条件之一中的任何一个,死锁问题就能被解决。 我们先来看一个示例,前面说过,死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。代码清单2所示的示例中,我们编写了一个简单的程序,它将会引起死锁发生,然后我们就会明白如何分析它。 清单2.死锁示例 publicclassThreadDeadlock publicstaticvoidmain(Stringargs)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运行输出 t1acquiringlockonjava.lang.object1dd3812 t1acquiredlockonjava.lang.object1dd3812 t2acquiringlockonjava.lang.objectc791b9 t2acquiredlockonjava.lang.objectc791b9 t3acquiringlockonjava.lang.object1aa9f99 t3acquiredlockonjava.lang.object1aa9f99 t1acquiringlockonjava.lang.objectc791b9 t2acquiringlockonjava.lang.object1aa9f99 在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际所用的应用中,发现死锁并将它排除是非常难的。 死锁情况诊断 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(Stringargs)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 rootfacenode4#jstack-l31274 xx-01-2912:40:27 FullthreaddumpJavaHotSpot(TM)64-BitServerVM(20.45-b01mixedmode): AttachListenerdaemonprio=10tid=0x00007f6d3c001000nid= 0x7a87waitingoncondition0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None DestroyJavaVMprio=10tid=0x00007f6da4006800nid= 0x7a2bwaitingoncondition0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None northprio=10tid=0x00007f6da4101800nid= 0x7a47waitingoncondition0x00007f6d8963b000 java.lang.Thread.State:WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor( 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: -(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) southprio=10tid=0x00007f6da4100000nid= 0x7a46waitingoncondition0x00007f6d8973c000 java.lang.Thread.State:WAITING(parking) atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor( 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: -(ajava.util.concurrent.locks.ReentrantLock$NonfairSync) LowMemoryDetectordaemonprio=10tid=0x00007f6da40d2800nid= 0x7a44runnable0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None C2CompilerThread1daemonprio=10tid=0x00007f6da40d0000nid= 0x7a43waitingoncondition0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None C2CompilerThread0daemonprio=10tid=0x00007f6da40cd000nid= 0x7a42waitingoncondition0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None SignalDispatcherdaemonprio=10tid=0x00007f6da40cb000nid= 0x7a41runnable0x0000000000000000 java.lang.Thread.State:RUNNABLE Lockedownablesynchronizers: -None Finalizerdaemonprio=10tid=0x00007f6da40af000nid= 0x7a40inobject.wait()0x00007f6d89d44000 java.lang.Thread.State:WAITING(onobjectmonitor) atjava.lang.object.wait(NativeMethod) -waitingon(ajava.lang.ref.ReferenceQueue$Lock) atjava.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) -locked(ajava.lang.ref.ReferenceQueue$Lock) atjava.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) atjava.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:171) Lockedownablesynchronizers: -None ReferenceHandlerdaemonprio=10tid=0x00007f6da40ad000nid= 0x7a3finobject.wait()0x00007f6d89e45000 java.lang.Thread.State:WAITING(onobjectmonitor) atjava.lang.object.wait(NativeMethod) -waitingon(ajava.lang.ref.Reference$Lock) atjava.lang.object.wait(object.java:485) atjava.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) -locked(ajava.lang.ref.Reference$Lock) Lockedownablesynchronizers: -None VMThreadprio=10tid=0x00007f6da40a6000nid=0x7a3erunnable GCtaskthread#0(ParallelGC)prio=10tid=0x00007f6da4019800nid=0x7a2crunnable GCtaskthread#1(ParallelGC)prio=10tid=0x00007f6da401b000nid=0x7a2drunnable GCtaskthread#2(ParallelGC)prio=10tid=0x00007f6da401d000nid=0x7a2erunnable GCtaskthread#3(ParallelGC)prio=10tid=0x00007f6da401f000nid=0x7a2frunnable GCtaskthread#4(ParallelGC)prio=10tid=0x00007f6da4020800nid=0x7a30runnable GCtaskthread#5(ParallelGC)prio=10tid=0x00007f6da4022800nid=0x7a31runnable GCtaskthread#6(ParallelGC)prio=10tid=0x00007f6da4024800nid=0x7a32runnable GCtaskthread#7(ParallelGC)prio=10tid=0x00007f6da4026000nid=0x7a33runnable GCtaskthread#8(ParallelGC)prio=10tid=0x00007f6da4028000nid=0x7a34runnable GCtaskthread#9(ParallelGC)prio=10tid=0x00007f6da402a000nid=0x7a35runnable GCtaskthread#10(ParallelGC)prio=10tid=0x00007f6da402b800nid=0x7a36runnable GCtaskthread#11(ParallelGC)prio=10tid=0x00007f6da402d800nid=0x7a37runnable GCtaskthread#12(ParallelGC)prio=10tid=0x00007f6da402f800nid=0x7a38runnable GCtaskthread#13(ParallelGC)prio=10tid=0x00007f6da4031000nid=0x7a39runnable GCtaskthread#14(ParallelGC)prio=10tid=0x00007f6da4033000nid=0x7a3arunnable GCtaskthread#15(ParallelGC)prio=10tid=0x00007f6da4035000nid=0x7a3brunnable GCtaskthread#16(ParallelGC)prio=10tid=0x00007f6da4036800nid=0x7a3crunnable GCtaskthread#17(ParallelGC)prio=10tid=0x00007f6da4038800nid=0x7a3drunnable VMPeriodicTaskThreadprio=10tid=0x00007f6da40dd000nid=0x7a45waitingoncondition JNIglobalreferences:886 清单6.jstack运行输出片段2 FoundoneJava-leveldeadlock: = north: waitingforownablesynchronizer0x000000075903c7c8,( ajava.util.concurrent.locks.ReentrantLock$NonfairSync), whichisheldbysouth south: waitingforownablesynchronizer0x000000075903c798,( ajava.util.concurrent.locks.ReentrantLock$NonfairSync), whichisheldbynorth Javastackinformationforthethreadslistedabove: = north: atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor( 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) south: atsun.misc.Unsafe.park(NativeMethod) -parkingtowaitfor( 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) Found1deadlock. 死锁解决方案 死锁是由四个必要条件导致的,所以一般来说,只要破坏这四个必要条件中的一个条件,死锁情况就应该不会发生。 如果想要打破互斥条件,我们需要允许进程同时访问某些资源,这种方法受制于实际场景,不太容易实现条件; 打破不可抢占条件,这样需要允许进程强行从占有者那里夺取某些资源,或者简单一点理解,占有资源的进程不能再申请占有其他资源,必须释放手上的资源之后才能发起申请,这个其实也很难找到适用场景; 进程在运行前申请得到所有的资源,否则该进程不能进入准备执行状态。这个方法看似有点用处,但是它的缺点是可能导致资源利用率和进程并发性降低; 避免出现资源申请环路,即对资源事先分类编号,按号分配。这种方式可以有效提高资源的利用率和系统吞吐量,但是增加了系统开销,增大了进程对资源的占用时间。 如果我们在死锁检查时发现了死锁情况,那么就要努力消除死锁,使系统从死锁状态中恢复过来。消除死锁的几种方式: 1.最简单、最常用的方法就是进行系统的重新启动,不过这种方法代价很大,它意味着在这之前所有的进程已经完成的计算工作都将付之东流,包括参与死锁的那些进程,以及未参与死锁的进程; 2.撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。这时又分两种情况:一次性撤消参与死锁的全部进程,剥夺全部资源;或者逐步撤消参与死锁的进程,逐步收回死锁进程占有的资源。一般来说,选择逐步撤消的进程时要按照一定的原则进行,目的是撤消那些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程运行时的代价和与此进程相关的外部作业的代价等因素;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 土石方工程公司合同付款管理办法
- 电子净化工程方案(3篇)
- 理发店运营入门知识培训课件
- 电泵节能改造工程方案(3篇)
- 110千伏桥头站增容改造工程环评报告
- 灯光工程管理装修方案(3篇)
- 安全教育礼仪培训课件
- 安全教育直播培训洛宁课件
- 防腐翻新工程方案(3篇)
- 高速出口打造工程方案(3篇)
- 第二单元混合运算单元测试卷(含答案) 2025-2026学年人教版三年级数学上册
- 出境人员行前安全培训课件
- 短视频个人劳务合同范本
- 纯电动汽车维护与保养 课件 模块一新能源汽车维护与保养基础认知
- 翻译后的基因表达调控
- 2025年度中国工商银行河南省分行社会招聘120人备考练习试题及答案解析
- (2025年标准)酒店政府采购协议书
- 苏教版三年级上册数学全册教学设计(配2025年秋新版教材)
- 重庆中医药学院2025年第二季度考核招聘工作人员笔试备考题库带答案详解
- 基孔肯雅热防护知识科普课件
- 中医优才考试试题及答案
评论
0/150
提交评论