死鎖詳解:常見(jiàn)死鎖案例抬伺、條件,如何避免出現(xiàn)死鎖灾梦?

一峡钓、什么是死鎖

死鎖不僅在個(gè)人學(xué)習(xí)中妓笙,甚至在開(kāi)發(fā)中也并不常見(jiàn)。但是一旦出現(xiàn)死鎖能岩,后果將非常嚴(yán)重寞宫。

首先什么是死鎖呢?打個(gè)比方捧灰,就好像有兩個(gè)人打架淆九,互相限制住了(鎖住,抱住)彼此一樣毛俏,互相動(dòng)彈不得,而且互相歐氣饲窿,你不松手我就不松手煌寇。好了誰(shuí)也動(dòng)彈不得。

在多線程的環(huán)境下逾雄,勢(shì)必會(huì)對(duì)資源進(jìn)行搶奪阀溶。當(dāng)兩個(gè)線程鎖住了當(dāng)前資源,但都需要對(duì)方的資源才能進(jìn)行下一步操作鸦泳,這個(gè)時(shí)候兩方就會(huì)一直等待對(duì)方的資源釋放银锻。這就形成了死鎖。這些永遠(yuǎn)在互相等待的進(jìn)程稱(chēng)為死鎖進(jìn)程做鹰。

那么我們來(lái)總結(jié)一下死鎖產(chǎn)生的條件:

互斥:資源的鎖是排他性的击纬,加鎖期間只能有一個(gè)線程擁有該資源。其他線程只能等待鎖釋放才能?chē)L試獲取該資源钾麸。

請(qǐng)求和保持:當(dāng)前線程已經(jīng)擁有至少一個(gè)資源更振,但其同時(shí)又發(fā)出新的資源請(qǐng)求,而被請(qǐng)求的資源被其他線程擁有饭尝。此時(shí)進(jìn)入保持當(dāng)前資源并等待下個(gè)資源的狀態(tài)肯腕。

不剝奪:線程已擁有的資源,只能由自己釋放钥平,不能被其他線程剝奪实撒。

循環(huán)等待:是指有多個(gè)線程互相的請(qǐng)求對(duì)方的資源,但同時(shí)擁有對(duì)方下一步所需的資源涉瘾。形成一種循環(huán)知态,類(lèi)似2)請(qǐng)求和保持。但此處指多個(gè)線程的關(guān)系睡汹。并不是指單個(gè)線程一直在循環(huán)中等待肴甸。

什么?還是不理解囚巴?那我們直接上代碼原在,動(dòng)手寫(xiě)一個(gè)死鎖友扰。

二、寫(xiě)一個(gè)死鎖

根據(jù)條件庶柿,我們讓兩個(gè)線程互相請(qǐng)求保持村怪。

/**

* 模擬死鎖場(chǎng)景

*/

public class DeadLockDemo implements Runnable{

? ? public static int flag = 1;

? ? //static 變量是 類(lèi)對(duì)象共享的

? ? static Object o1 = new Object();

? ? static Object o2 = new Object();

? ? @Override

? ? public void run() {

? ? ? ? System.out.println(Thread.currentThread().getName() + ":此時(shí) flag = " + flag);

? ? ? ? if(flag == 1){

? ? ? ? ? ? synchronized (o1){

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? System.out.println("我是" + Thread.currentThread().getName() + "鎖住 o1");

? ? ? ? ? ? ? ? ? ? Thread.sleep(3000);

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "醒來(lái)->準(zhǔn)備獲取 o2");

? ? ? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? synchronized (o2){

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "拿到 o2");//第24行

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? if(flag == 0){

? ? ? ? ? ? synchronized (o2){

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? System.out.println("我是" + Thread.currentThread().getName() + "鎖住 o2");

? ? ? ? ? ? ? ? ? ? Thread.sleep(3000);

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "醒來(lái)->準(zhǔn)備獲取 o1");

? ? ? ? ? ? ? ? }catch (Exception e){

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? synchronized (o1){

? ? ? ? ? ? ? ? ? ? System.out.println(Thread.currentThread().getName() + "拿到 o1");//第38行

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? public static? void main(String args[]){

? ? ? ? DeadLockDemo t1 = new DeadLockDemo();

? ? ? ? DeadLockDemo t2 = new DeadLockDemo();

? ? ? ? t1.flag = 1;

? ? ? ? new Thread(t1).start();

? ? ? ? //讓main線程休眠1秒鐘,保證t2開(kāi)啟鎖住o2.進(jìn)入死鎖

? ? ? ? try {

? ? ? ? ? ? Thread.sleep(1000);

? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? t2.flag = 0;

? ? ? ? new Thread(t2).start();

? ? }

代碼中,

t1創(chuàng)建浮庐,t1先拿到o1的鎖甚负,開(kāi)始休眠3秒。然后

t2線程創(chuàng)建审残,t2拿到o2的鎖梭域,開(kāi)始休眠3秒。然后

t1先醒來(lái)搅轿,準(zhǔn)備拿o2的鎖病涨,發(fā)現(xiàn)o2已經(jīng)加鎖,只能等待o2的鎖釋放璧坟。

t2后醒來(lái)既穆,準(zhǔn)備拿o1的鎖,發(fā)現(xiàn)o1已經(jīng)加鎖雀鹃,只能等待o1的鎖釋放幻工。

t1,t2形成死鎖。

我們查看運(yùn)行狀態(tài)

三黎茎、發(fā)現(xiàn)排查死鎖情況

我們利用jdk提供的工具定位死鎖問(wèn)題:

jps顯示所有當(dāng)前Java虛擬機(jī)進(jìn)程名及pid.

jstack打印進(jìn)程堆棧信息囊颅。

列出所有java進(jìn)程。

我們檢查一下DeadLockDemo工三,為什么這個(gè)線程不退棧迁酸。

jstack 11170

我們直接翻到最后:已經(jīng)檢測(cè)出了一個(gè)java級(jí)別死鎖。其中兩個(gè)線程分別卡在了代碼第27行和第41行俭正。檢查我們代碼的對(duì)應(yīng)位置奸鬓,即可排查錯(cuò)誤。此處我們是第二個(gè)鎖始終拿不到掸读,所以死鎖了串远。

在這里插播一下,還是有很多朋友對(duì)死鎖不了解儿惫,為了讓大家在之后的學(xué)習(xí)或者工作中避免出現(xiàn)死鎖不知道該如何解決澡罚。我在這里寫(xiě)一些常見(jiàn)的死鎖是怎么產(chǎn)生的便于大家避免,遇到相似的情況可以很好的解決肾请。后續(xù)也會(huì)持續(xù)更新Java的其它知識(shí)點(diǎn)和面試方法資料等等...感興趣的鐵汁們可以持續(xù)關(guān)注我

常見(jiàn)的死鎖是如何產(chǎn)生的留搔,如何避免

概述:

什么場(chǎng)景下回發(fā)生 00060 死鎖問(wèn)題:

一般情況下,數(shù)據(jù)庫(kù)自身發(fā)生死鎖的情況很少铛铁,一般情況都是因?yàn)閼?yīng)用本身調(diào)用問(wèn)題導(dǎo)致的 00060異常 隔显。

比如說(shuō)有兩個(gè)會(huì)話(huà)sid却妨,分別為 138 和136,這兩個(gè)會(huì)話(huà)都要對(duì) 6677 和 7788 兩個(gè)人加工資括眠,但是執(zhí)行的順序不一樣彪标,操作分別是:

-- 會(huì)話(huà)session號(hào) Session 1 (sid = 136), Session 2 (sid = 138)

-- 執(zhí)行的語(yǔ)句 Session 1 (sid = 136)

update emp set sal=sal+100 where empno=6677;

update emp set sal=sal+100 where empno=7788;

-- 執(zhí)行的語(yǔ)句 Session 2 (sid = 138)

update emp set sal=sal+100 where empno=7788;

update emp set sal=sal+100 where empno=6677;

ORA-00060:deadlock detected while waiting for resource

這樣我們就成功的觸發(fā)了一個(gè)ORA-00060掷豺。

出現(xiàn)這個(gè)問(wèn)題捞烟,我們可以查看Oracle日志,日志路徑:$ORACLE_BASE/diag/rdbms/org11/ora11/trace/alert*.log

可以從告警日志中看到很多類(lèi)似如下的日志:

ORA-00060:Deadlock detected.More info in file

ORA-00060:Deadlock detected.More info in file /home1/oracle/diag/rdbms/ora11g/ora11g/trace/ora11g_ora_14757.trc

我們看下對(duì)飲的trc日志当船,主要看Deadlock graph题画,其中:可以看到136和138互相死鎖,session 138(也就是 session2生年,sid=138)等著要 ROWID=AAAMfAAAAgAAA的行鎖婴程,而 session136(也就是 session1,sid=136)等著要ROWID=AAAMfPAAEAAAAgAAL 的行抱婉。

處理方式:

1. 給資源編號(hào),然后按照固定的順序進(jìn)行訪問(wèn)桌粉。

簡(jiǎn)單的來(lái)說(shuō)蒸绩,就是先改編號(hào)小的,在改編號(hào)大的铃肯。當(dāng)然患亿,反著來(lái)也可以。

-- 會(huì)話(huà)session號(hào) Session 1 (sid = 136)押逼, Session 2 (sid = 138)

update emp set sal=sal+100 where empno=6677;

update emp set sal=sal+100 where empno=7788;

Waiting...

update emp set sal=sal+100 where empno=7788;

commit/rollback;

update emp set sal=sal+100 where empno=6677;

在這里步藕,工資增加兩次,但是 session 2 被 session 1 阻塞了挑格,對(duì)于用戶(hù)體驗(yàn)來(lái)說(shuō)咙冗,感受不好。

如果 session 1 一直不結(jié)束事務(wù)漂彤,session 2 只能一直等下去雾消,這樣比deadlock 后,Oracle 程序本身出面調(diào)停還要糟糕挫望。

2. 可以在 select … for update nowait 語(yǔ)句測(cè)試一下需要更改的行是否被鎖定

如果沒(méi)有被鎖定立润,那這個(gè)語(yǔ)句會(huì)馬上給這行加鎖,如果已經(jīng)加鎖那就馬上返回:ORA-00054:resource busy and acquire with NOWAIT specified 媳板,如下表所示:

-- 會(huì)話(huà)session號(hào) Session 1 (sid = 136)桑腮, Session 2 (sid = 138)

select * from emp where empno in(6677,7788) for update nowait;

select * from emp where empno in(6677,7788) for update nowait;

ORA-00054:resource busy and acquire with NOWAIT specified

update emp set sal=sal+100 where empno=6677;

update emp set sal=sal+100 where empno=7788;

方法一和方法二都存在一定的問(wèn)題,特別是在ND代碼中如果使用方法二那么修改起來(lái)工作量太大蛉幸,但是如果我們不處理破讨,Oracle有自動(dòng)檢測(cè)死鎖并且回滾事務(wù)的功能丛晦,也就是說(shuō)之前的會(huì)話(huà)中136 和138 有一個(gè)會(huì)成功,一個(gè)會(huì)回滾添忘,返回失敗采呐,這樣就保證了數(shù)據(jù)的一致性。

總結(jié):

對(duì)應(yīng)上面兩處處理方式搁骑。感覺(jué)都不好斧吐,畢竟現(xiàn)網(wǎng)這種場(chǎng)景較少。而且這種死鎖不是永久性的一直卡死在這仲器,Oracle會(huì)檢測(cè)到這種死鎖的煤率,并且檢測(cè)到后會(huì)自己回滾,所以直接交給Oracle即可乏冀。

附相關(guān)的查詢(xún)SQL:

1.查詢(xún)死鎖:

select t2.username,t2.sid,t2.seria#,t2.logon_time

from v$locked_object t1,v$session t2

where t1.session_id = t2.sid

order by t2.logon_time;

2.根據(jù) sid 查詢(xún)對(duì)應(yīng)的SQL語(yǔ)句蝶糯,比如第一點(diǎn)查詢(xún)出 sid 為136 和138 的死鎖結(jié)果:

select sql_text

from v$session a,$sqltext_with_newlines b

where DECODE(a.sql_hash_value,0,prev_hash_value,sql_hash_value) = b.hash_value and a.sid in ('136','138')

order by piece;

3.查看處于等待狀態(tài)的SQL語(yǔ)句:

select a.spid,c.EVENT,b.LOGON_TIME,d.SQL_TEXT,a.PROGRAM

from v$process a,v$session b,v$session_wait c,v$sql d

where a.ADDR = b.PADDR and b.SID = c.SID

? and b.SQL_HASH_VALUE = D.HASH_VALUE

? and c.EVENT NOT LIKE '%SQL*Net%'

? and c.EVENT NOT LIKE '%smon%'

? and c.EVENT NOT LIKE '%jopq%'

? and c.EVENT NOT LIKE '%ipc%'

4.查看目前是否有“長(zhǎng)時(shí)間持有鎖未釋放”,必要的情況下可以用對(duì)應(yīng)的 command 殺死 session:

select I.BLOCK,ILMODE,I.REQUEST,I.TYPE,I.ID1,I.CTIME,s.SID,s.SERIAL#,

T.SQL_TEXT,p.SPID,'ALTER SYSTEM KILL SESSION'"||S.SID||','||s.serial#||"',' Command

from v$lock i,v$SESSION s,v$SQL t,v$process p

where I.ID1 in (select id1 from v$lock where block = 1)

? and I.SID = s.SID and (t.hash_value = DECODE(s.sql_hash_value,0,s.prev_hash_value,s.sql_hash_value))

? and t.address = DECODE(a.sql_hash_value,0,s.prev_hash_addr,s.sql_address))

? and p.ADDR = s.PADDR

order by I.ID1,I.CTIME desc;

5.殺死進(jìn)程(396為sid辆沦,60589為serial#)昼捍,該條可以結(jié)合第一點(diǎn)進(jìn)行:

alter system kill session '396,60589';

四、解決辦法

死鎖一旦發(fā)生肢扯,我們就無(wú)法解決了妒茬。所以我們只能避免死鎖的發(fā)生。

既然死鎖需要滿(mǎn)足四種條件蔚晨,那我們就從條件下手乍钻,只要打破任意規(guī)則即可。

互斥)盡量少用互斥鎖铭腕,能加讀鎖银择,不加寫(xiě)鎖。當(dāng)然這條無(wú)法避免累舷。

請(qǐng)求和保持)采用資源靜態(tài)分配策略(進(jìn)程資源靜態(tài)分配方式是指一個(gè)進(jìn)程在建立時(shí)就分配了它需要的全部資源).我們盡量不讓線程同時(shí)去請(qǐng)求多個(gè)鎖浩考,或者在擁有一個(gè)鎖又請(qǐng)求不到下個(gè)鎖時(shí),不保持等待笋粟,先釋放資源等待一段時(shí)間在重新請(qǐng)求怀挠。

不剝奪)允許進(jìn)程剝奪使用其他進(jìn)程占有的資源。優(yōu)先級(jí)害捕。

循環(huán)等待)盡量調(diào)整獲得鎖的順序绿淋,不發(fā)生嵌套資源請(qǐng)求。加入超時(shí)尝盼。

本文到此結(jié)束啦吞滞,喜歡的鐵子們可以點(diǎn)點(diǎn)贊和關(guān)注, 文章持續(xù)更新,也可以評(píng)論出你想看哪一塊技術(shù)裁赠。鐵子們的支持是我的動(dòng)力殿漠,創(chuàng)作離不開(kāi)鐵子們的支持,在此先感謝大家佩捞!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绞幌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子一忱,更是在濱河造成了極大的恐慌莲蜘,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帘营,死亡現(xiàn)場(chǎng)離奇詭異票渠,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)芬迄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)问顷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人禀梳,你說(shuō)我怎么就攤上這事杜窄。” “怎么了算途?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵羞芍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我郊艘,道長(zhǎng),這世上最難降的妖魔是什么唯咬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任纱注,我火速辦了婚禮,結(jié)果婚禮上胆胰,老公的妹妹穿的比我還像新娘狞贱。我一直安慰自己,他們只是感情好蜀涨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布瞎嬉。 她就那樣靜靜地躺著,像睡著了一般厚柳。 火紅的嫁衣襯著肌膚如雪氧枣。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天别垮,我揣著相機(jī)與錄音便监,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛烧董,可吹牛的內(nèi)容都是我干的毁靶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼逊移,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼预吆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起胳泉,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拐叉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胶背,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體巷嚣,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年钳吟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了廷粒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡红且,死狀恐怖坝茎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情暇番,我是刑警寧澤嗤放,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站壁酬,受9級(jí)特大地震影響次酌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舆乔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一岳服、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧希俩,春花似錦吊宋、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鳞上,卻和暖如春这吻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背因块。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工橘原, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓趾断,卻偏偏與公主長(zhǎng)得像拒名,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芋酌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容