Java中死鎖的原理實(shí)戰(zhàn)分析
本文實(shí)例講述了Java中死鎖的原理。分享給大家供大家參考,具體如下:
一 點(diǎn)睛
當(dāng)兩個(gè)線程相互等待對(duì)方釋放同步監(jiān)視器時(shí)就會(huì)發(fā)生死鎖,Java虛擬機(jī)沒(méi)有監(jiān)測(cè)、也沒(méi)有采用措施來(lái)處理死鎖情況,所以多線程編程時(shí)應(yīng)該采取措施避免死鎖的出現(xiàn)。
一旦出現(xiàn)死鎖,整個(gè)程序既不會(huì)發(fā)生任何異常,也不會(huì)給出任何提示,只是所有線程處于阻塞狀態(tài),無(wú)法繼續(xù)。
二 代碼
class A
{
public synchronized void foo( B b )
{
System.out.println("當(dāng)前線程名: " + Thread.currentThread().getName()
+ " 進(jìn)入了A實(shí)例的foo()方法" ); // ①
try
{
Thread.sleep(200);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("當(dāng)前線程名: " + Thread.currentThread().getName()
+ " 企圖調(diào)用B實(shí)例的last()方法"); // ③
b.last();
}
public synchronized void last()
{
System.out.println("進(jìn)入了A類的last()方法內(nèi)部");
}
}
class B
{
public synchronized void bar( A a )
{
System.out.println("當(dāng)前線程名: " + Thread.currentThread().getName()
+ " 進(jìn)入了B實(shí)例的bar()方法" ); // ②
try
{
Thread.sleep(200);
}
catch (InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("當(dāng)前線程名: " + Thread.currentThread().getName()
+ " 企圖調(diào)用A實(shí)例的last()方法"); // ④
a.last();
}
public synchronized void last()
{
System.out.println("進(jìn)入了B類的last()方法內(nèi)部");
}
}
public class DeadLock implements Runnable
{
A a = new A();
B b = new B();
public void init()
{
Thread.currentThread().setName("主線程");
// 調(diào)用a對(duì)象的foo方法
a.foo(b);
System.out.println("進(jìn)入了主線程之后");
}
public void run()
{
Thread.currentThread().setName("副線程");
// 調(diào)用b對(duì)象的bar方法
b.bar(a);
System.out.println("進(jìn)入了副線程之后");
}
public static void main(String[] args)
{
DeadLock dl = new DeadLock();
// 以dl為target啟動(dòng)新線程
new Thread(dl).start();
// 調(diào)用init()方法
dl.init();
}
}
三 運(yùn)行結(jié)果
當(dāng)前線程名: 主線程 進(jìn)入了A實(shí)例的foo()方法
當(dāng)前線程名: 副線程 進(jìn)入了B實(shí)例的bar()方法
當(dāng)前線程名: 主線程 企圖調(diào)用B實(shí)例的last()方法
當(dāng)前線程名: 副線程 企圖調(diào)用A實(shí)例的last()方法
四 說(shuō)明
從運(yùn)行結(jié)果來(lái)看,程序無(wú)法向下執(zhí)行,也不會(huì)拋出任何異常,就一直“僵持”者。
上面代碼中的A對(duì)象和B對(duì)象的方法都是同步方法,也就是A對(duì)象和B對(duì)象都是同步鎖。
程序中有兩個(gè)線程執(zhí)行,一個(gè)線程的線程執(zhí)行體是DeadLock類的run()方法,另外一個(gè)是DeadLock的init()方法(主線程調(diào)用init()方法)。其中run()方法讓B對(duì)象調(diào)用bar()方法,而init()方法讓A對(duì)象調(diào)用foo()方法。
程序運(yùn)行的流程如下:
- 1 init()方法先執(zhí)行,調(diào)用A對(duì)象的foo()方法,進(jìn)入foo()方法之前,該線程對(duì)A對(duì)象加鎖,進(jìn)入foo()方法后,打印一下,然后暫停執(zhí)行200ms
- 2 CPU切換到另外一個(gè)線程,讓B對(duì)象執(zhí)行bar方法,進(jìn)入bar()方法之前,該線程對(duì)B對(duì)象加鎖,進(jìn)入bar()方法后,打印一下,然后暫停執(zhí)行200ms
- 3 主線程先醒過(guò)來(lái),繼續(xù)向下執(zhí)行,當(dāng)調(diào)用B對(duì)象的last方法時(shí),會(huì)被阻塞,因?yàn)榇藭r(shí)必須對(duì)B對(duì)象進(jìn)行加鎖,但此時(shí)副線程正保持B對(duì)象的鎖,所以此時(shí)主線程會(huì)一直等待。
- 4 副線程會(huì)醒過(guò)來(lái),會(huì)繼續(xù)往下執(zhí)行,當(dāng)調(diào)用A對(duì)象的last方法時(shí),會(huì)被阻塞,因?yàn)榇藭r(shí)必須對(duì)A對(duì)象加鎖,但此時(shí)主線程正保持A對(duì)象的鎖,所以此時(shí)副線程會(huì)一直等待。
- 5 兩個(gè)線程互相等待對(duì)方先釋放,所以出現(xiàn)了死鎖。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java進(jìn)程與線程操作技巧總結(jié)》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
在spring?boot3中使用native?image的最新方法
這篇文章主要介紹了在spring?boot3中使用native?image?,今天我們用具體的例子來(lái)給大家演示一下如何正確的將spring boot3的應(yīng)用編譯成為native image,需要的朋友可以參考下2023-01-01
JavaWeb入門:ServletContext詳解和應(yīng)用
這篇文章主要介紹了Java ServletContext對(duì)象用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-07-07
MybatisPlus3.3.0沒(méi)有MybatisPlusInterceptor類問(wèn)題的解決方法
項(xiàng)目使用的是mybatis-plus-extension3.3.0依賴,然后在我使用分頁(yè)插件的時(shí)候,發(fā)現(xiàn)無(wú)法導(dǎo)入MybatisPlusInterceptor類所以本文給大家介紹了MybatisPlus3.3.0沒(méi)有MybatisPlusInterceptor類問(wèn)題的解決方法,需要的朋友可以參考下2023-12-12
使用Maven 搭建 Spring MVC 本地部署Tomcat的詳細(xì)教程
這篇文章主要介紹了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
Spring循環(huán)依賴實(shí)現(xiàn)過(guò)程揭秘
這篇文章主要介紹了Spring循環(huán)依賴實(shí)現(xiàn)過(guò)程,Spring的解決循環(huán)依賴是有前置條件的,要解決循環(huán)依賴我們首先要了解Spring Bean對(duì)象的創(chuàng)建過(guò)程和依賴注入的方式2023-01-01

