您好,登錄后才能下訂單哦!
事務(wù)的必要性
為了避免出現(xiàn)數(shù)據(jù)不一致問(wèn)題,需要在存儲(chǔ)過(guò)程中引入事務(wù)的概念,將更新語(yǔ)句綁在一起,讓它們成為一個(gè)”原子性”的操作:更新語(yǔ)句要么都執(zhí)行,要么都不執(zhí)行。
一、關(guān)閉MySQL自動(dòng)提交的方法
1、顯示關(guān)閉自動(dòng)提交
set autocommit=0;1
2、隱式關(guān)閉自動(dòng)提交:(推薦)
start transaction1
一般推薦使用隱式的提交方式,因?yàn)椴粫?huì)修改到autocommit系統(tǒng)變量。
二、關(guān)閉自動(dòng)提交后,提交更新語(yǔ)句的方法有
1.顯示地提交:(推薦)
commit;1
2.隱式地提交
包括了begin、set autocommit=1、start transaction、rename table、truncate table等語(yǔ)句;
數(shù)據(jù)定義(create、alter、drop)function、alter procedure、drop database、drop table、drop function、drop index、drop procedure等;
權(quán)限管理和賬戶管理語(yǔ)句(grant、revoke、set password、create user、drop user、rename user)
鎖語(yǔ)句(lock tables、unlock tables)
更推薦用顯示提交地方式。
三、事務(wù)處理使用方法
在處理錯(cuò)誤代碼地程序中使用rollback(事務(wù)回滾)。
在具有原子性操作的地方,使用start transaction隱式地關(guān)閉自動(dòng)提交,并且在結(jié)束的為止上使用commit顯示提交。
四、事務(wù)保存點(diǎn)的使用方法
在事務(wù)中使用savepoint 保存點(diǎn)名 的格式創(chuàng)建保存點(diǎn),實(shí)現(xiàn)事務(wù)的”部分”提交或”部分”撤銷(rollback to savepoint 保存點(diǎn)名)。
保存點(diǎn)是”臨時(shí)狀態(tài)”,既可以回滾到事務(wù)開(kāi)始前的狀態(tài),也能決定事務(wù)的下一個(gè)狀態(tài),是介于兩自動(dòng)提交語(yǔ)句所引發(fā)狀態(tài)中的一種臨時(shí)狀態(tài)。
五、鎖機(jī)制的必要性
內(nèi)存中的數(shù)據(jù)與外存中的數(shù)據(jù)不同步,其中的表記錄之間存在“同步延遲“。
六、MyISAM表施加表級(jí)鎖的語(yǔ)法格式
lock tables 表1 [as 別名] read [local]
[,表2[ as 別名2][low_priority] write] ...12
其中read local與read選項(xiàng)的差別為 READ LOCAL允許在鎖定被保持時(shí),執(zhí)行非沖突性INSERT語(yǔ)句(同時(shí)插入)。
七、鎖的粒度、隱式鎖與顯示鎖、鎖的類型、鎖的鑰匙、鎖的生命周期
1.鎖的粒度指鎖的作用范圍:
多個(gè)MySQL客戶機(jī)并發(fā)訪問(wèn)同一個(gè)數(shù)據(jù)時(shí),為保證數(shù)據(jù)的一致性,數(shù)據(jù)庫(kù)管理系統(tǒng)會(huì)自動(dòng)地為該數(shù)據(jù)加鎖、解鎖,這種是隱式鎖。
而有時(shí)單靠隱式鎖無(wú)法實(shí)現(xiàn)數(shù)據(jù)的一致性訪問(wèn)要求(例如對(duì)于臨界資源的爭(zhēng)奪上),此時(shí)需要手動(dòng)地加鎖、解鎖,這種稱為顯示鎖。
2.鎖的類型分為:讀鎖(共享鎖)和寫(xiě)鎖(排他鎖或獨(dú)占鎖)
3.鎖的鑰匙: 當(dāng)多個(gè)MySQL客戶機(jī)并發(fā)訪問(wèn)同一個(gè)數(shù)據(jù)時(shí)、如果MySQL客戶機(jī)A對(duì)該數(shù)據(jù)成功地施加了鎖,那么只有MySQL客戶機(jī)A擁有這把鎖的鑰匙。
4.鎖的生命周期:在同一個(gè)MySQL會(huì)話內(nèi),對(duì)數(shù)據(jù)加鎖到解鎖之間的時(shí)間間隔。鎖的生命周期越長(zhǎng),并發(fā)訪問(wèn)性能越低;鎖的生命周期越短,并發(fā)訪問(wèn)性能越高。
八、InnoDB表施加行級(jí)鎖的語(yǔ)法格式
共享鎖 select * from 表 where 條件語(yǔ)句 lock in share mode;
排他鎖 select * from 表 where 條件語(yǔ)句 for update;
九、InnoDB中的間隙鎖、記錄鎖
當(dāng)檢索條件滿足某區(qū)間范圍,但表中不存在的記錄,此時(shí)也有共享鎖或排他鎖,即行級(jí)鎖會(huì)鎖定相鄰的鍵,這種機(jī)制就是間隙鎖(next-key鎖)
當(dāng)事務(wù)隔離級(jí)別設(shè)置為repeatable read,此時(shí)InnoDB表施加行級(jí)鎖,默認(rèn)使用間隔鎖(需要索引),
當(dāng)事務(wù)的隔離級(jí)別設(shè)置為read uncommited或者read commited,此時(shí)InnoDB表施加行級(jí)鎖,默認(rèn)情況下使用記錄鎖(record lock)。
與間隙鎖不同,記錄鎖僅僅為滿足該查詢范圍的記錄施加共享鎖或排他鎖。
十、鎖等待與死鎖
一)鎖等待:是為了保證事務(wù)可以正常地并發(fā)運(yùn)行,鎖等待不一定導(dǎo)致死鎖問(wèn)題的發(fā)生。而死鎖問(wèn)題的發(fā)生一定伴隨著鎖等待現(xiàn)象。
二)死鎖:
1、定義: 是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。當(dāng)線程進(jìn)入對(duì)象的synchronized代碼塊時(shí),便占有了資源,直到它退出該代碼塊或者調(diào)用wait方法,才釋放資源,在此期間,其他線程將不能進(jìn)入該代碼塊。當(dāng)線程互相持有對(duì)方所需要的資源時(shí),會(huì)互相等待對(duì)方釋放資源,如果線程都不主動(dòng)釋放所占有的資源,將產(chǎn)生死鎖。
2、死鎖的產(chǎn)生4個(gè)必要條件:
1)互斥條件:進(jìn)程對(duì)于所分配到的資源具有排它性,即一個(gè)資源只能被一個(gè)進(jìn)程占用,直到被該進(jìn)程釋放
2)占有且等待:一個(gè)進(jìn)程因請(qǐng)求被占用資源而發(fā)生阻塞時(shí),對(duì)已獲得的資源保持不放。
3)不可搶占條件:任何一個(gè)資源在沒(méi)被該進(jìn)程釋放之前,任何其他進(jìn)程都無(wú)法對(duì)他剝奪占用
4)循環(huán)等待條件:當(dāng)發(fā)生死鎖時(shí),所等待的進(jìn)程必定會(huì)形成一個(gè)環(huán)路(類似于死循環(huán)),造成永久阻塞。
3、死鎖的另一種:遞歸死鎖,舉例:
遞歸函數(shù)就是自調(diào)用函數(shù),在函數(shù)體內(nèi)直接或間接的調(diào)用自己,即函數(shù)的嵌套是函數(shù)本身。
遞歸方式有兩種:直接遞歸和間接遞歸,直接遞歸就是在函數(shù)中出現(xiàn)調(diào)用函數(shù)本身。間接遞歸,指函數(shù)中調(diào)用了其他函數(shù),而該其他函數(shù)又調(diào)用了本函數(shù)。
那什么時(shí)候使用遞歸呢?一般來(lái)說(shuō)當(dāng)你要在某段代碼邏輯中使用循環(huán)迭代的時(shí)候但是迭代的次數(shù)在迭代之前無(wú)法知曉的情況下使用遞歸。打個(gè)比方你要在一個(gè)文件夾中查找某個(gè)文件,而這個(gè)文件夾底下有N多子文件夾和文件,當(dāng)你在不知道有多少層文件夾和文件的情況下你就得用到遞歸了。
遞歸的優(yōu)點(diǎn)就是讓代碼顯得很簡(jiǎn)潔,同時(shí)有些應(yīng)用場(chǎng)景不得不使用遞歸比如前面說(shuō)的找文件。遞歸是個(gè)好東西但是在某些時(shí)候也會(huì)給你帶來(lái)一些麻煩。比如在多線程的環(huán)境下使用遞歸,遇到了多線程那么就不得不面對(duì)同步的問(wèn)題。而遞歸程序遇到同步的時(shí)候很容易出問(wèn)題。
多線程的遞歸就是指遞歸鏈中的某個(gè)方法由另外一個(gè)線程來(lái)操作,以下代碼的意思都是這個(gè)意思即調(diào)用recursive()和businessLogic()并非一個(gè)線程(如果是在一個(gè)線程中就不存在死鎖問(wèn)題,例如下面的recursive變成private就不存在問(wèn)題。)
[java] view plain copy
public class Test {
public void recursive(){
this.businessLogic();
}
public synchronized void businessLogic(){
System.out.println("處理業(yè)務(wù)邏輯");
System.out.println("保存到<a href="http://lib.csdn.net/base/mysql" class='replace_word' title="MySQL知識(shí)庫(kù)" target='_blank' style='color:#df3434; font-weight:bold;'>數(shù)據(jù)庫(kù)</a>");
this.recursive();
}
}
以上這段代碼就是個(gè)能形成死鎖的代碼,事實(shí)上這個(gè)“synchronized”放在“businessLogic()”和“recursive()”都會(huì)形成死鎖,并且是多線程的情況下就會(huì)鎖??!他的邏輯順序是先執(zhí)行recursive()方法然后接下來(lái)執(zhí)行businessLogic()方法同時(shí)將businessLogic()方法鎖住,接下來(lái)程序進(jìn)入businessLogic()方法內(nèi)部執(zhí)行完打印語(yǔ)句后開(kāi)始執(zhí)行recursive(),進(jìn)入recursive()后準(zhǔn)備執(zhí)行businessLogic(),等等問(wèn)題來(lái)了!之前執(zhí)行的businessLogic()的鎖還沒(méi)有放開(kāi)這次又執(zhí)行到這里了,當(dāng)然是過(guò)不去的了,形成了死鎖!從這個(gè)例子我們總結(jié)出來(lái)一個(gè)規(guī)律就是在遞歸的時(shí)候在遞歸鏈上面的方法上加鎖肯定會(huì)出現(xiàn)死鎖(所謂遞歸鏈就是指recursive()鏈向businessLogic(),businessLogic()又鏈回recursive()),解決這個(gè)問(wèn)題的方法就是避免在遞歸鏈上加鎖,請(qǐng)看以下的例子
[java] view plain copy
public class Test {
public void recursive(){
this.businessLogic();
}
public void businessLogic(){
System.out.println("處理業(yè)務(wù)邏輯");
this.saveToDB();
this.recursive();
}
public synchronized void saveToDB(){
System.out.println("保存到數(shù)據(jù)庫(kù)");
}
}
saveToDB()不在這條遞歸鏈上面自然不會(huì)出現(xiàn)死鎖,所以說(shuō)在遞歸中加鎖是件很危險(xiǎn)的事情,實(shí)在逃不過(guò)要加鎖就加在最小的粒度的程序代碼上以減小死鎖的概率。
4、處理死鎖的方法
4.1 預(yù)防死鎖
4.2 避免死鎖
4.2.1 常用避免死鎖的方法
4.2.1.1 有序資源分配法
4.2.1.2 銀行家算法
4.2.2 常用避免死鎖的技術(shù)
4.2.2.1 加鎖順序
4.2.2.2 加鎖時(shí)限
4.2.2.3 死鎖檢測(cè)
4.3 檢測(cè)死鎖
4.4 解除死鎖
處理死鎖的方法
4.1、死鎖預(yù)防 ----- 確保系統(tǒng)永遠(yuǎn)不會(huì)進(jìn)入死鎖狀態(tài) 產(chǎn)生死鎖需要四個(gè)條件,那么,只要這四個(gè)條件中至少有一個(gè)條件得不到滿足,就不可能發(fā)生死鎖了。由于互斥條件是非共享資源所必須的,不僅不能改變,還應(yīng)加以保證,所以,主要是破壞產(chǎn)生死鎖的其他三個(gè)條件。
a、破壞“占有且等待”條件
方法1:所有的進(jìn)程在開(kāi)始運(yùn)行之前,必須一次性地申請(qǐng)其在整個(gè)運(yùn)行過(guò)程中所需要的全部資源。
優(yōu)點(diǎn):簡(jiǎn)單易實(shí)施且安全。
缺點(diǎn):因?yàn)槟稠?xiàng)資源不滿足,進(jìn)程無(wú)法啟動(dòng),而其他已經(jīng)滿足了的資源也不會(huì)得到利用,嚴(yán)重降低了資源的利用率,造成資源浪費(fèi)。 使進(jìn)程經(jīng)常發(fā)生饑餓現(xiàn)象。
方法2:該方法是對(duì)第一種方法的改進(jìn),允許進(jìn)程只獲得運(yùn)行初期需要的資源,便開(kāi)始運(yùn)行,在運(yùn)行過(guò)程中逐步釋放掉分配到的已經(jīng)使用完畢的資源,然后再去請(qǐng)求新的資源。這樣的話,資源的利用率會(huì)得到提高,也會(huì)減少進(jìn)程的饑餓問(wèn)題。
b、破壞“不可搶占”條件 當(dāng)一個(gè)已經(jīng)持有了一些資源的進(jìn)程在提出新的資源請(qǐng)求沒(méi)有得到滿足時(shí),它必須釋放已經(jīng)保持的所有資源,待以后需要使用的時(shí)候再重新申請(qǐng)。這就意味著進(jìn)程已占有的資源會(huì)被短暫地釋放或者說(shuō)是被搶占了。 該種方法實(shí)現(xiàn)起來(lái)比較復(fù)雜,且代價(jià)也比較大。釋放已經(jīng)保持的資源很有可能會(huì)導(dǎo)致進(jìn)程之前的工作實(shí)效等,反復(fù)的申請(qǐng)和釋放資源會(huì)導(dǎo)致進(jìn)程的執(zhí)行被無(wú)限的推遲,這不僅會(huì)延長(zhǎng)進(jìn)程的周轉(zhuǎn)周期,還會(huì)影響系統(tǒng)的吞吐量。
c、破壞“循環(huán)等待”條件 可以通過(guò)定義資源類型的線性順序來(lái)預(yù)防,可將每個(gè)資源編號(hào),當(dāng)一個(gè)進(jìn)程占有編號(hào)為i的資源時(shí),那么它下一次申請(qǐng)資源只能申請(qǐng)編號(hào)大于i的資源。如圖所示:這樣雖然避免了循環(huán)等待,但是這種方法是比較低效的,資源的執(zhí)行速度回變慢,并且可能在沒(méi)有必要的情況下拒絕資源的訪問(wèn),比如說(shuō),進(jìn)程c想要申請(qǐng)資源1,如果資源1并沒(méi)有被其他進(jìn)程占有,此時(shí)將它分配個(gè)進(jìn)程c是沒(méi)有問(wèn)題的,但是為了避免產(chǎn)生循環(huán)等待,該申請(qǐng)會(huì)被拒絕,這樣就降低了資源的利用率
4.2、避免死鎖 ----- 在使用前進(jìn)行判斷,只允許不會(huì)產(chǎn)生死鎖的進(jìn)程申請(qǐng)資源的死鎖避免是利用額外的檢驗(yàn)信息,在分配資源時(shí)判斷是否會(huì)出現(xiàn)死鎖,只在不會(huì)出現(xiàn)死鎖的情況下才分配資源。
兩種避免辦法:
1、如果一個(gè)進(jìn)程的請(qǐng)求會(huì)導(dǎo)致死鎖,則不啟動(dòng)該進(jìn)程
2、如果一個(gè)進(jìn)程的增加資源請(qǐng)求會(huì)導(dǎo)致死鎖 ,則拒絕該申請(qǐng)。
注意:預(yù)防死鎖和避免死鎖的區(qū)別:
預(yù)防死鎖是設(shè)法至少破壞產(chǎn)生死鎖的四個(gè)必要條件之一,嚴(yán)格的防止死鎖的出現(xiàn),
而避免死鎖則不那么嚴(yán)格的限制產(chǎn)生死鎖的必要條件的存在,因?yàn)榧词顾梨i的必要條件存在,也不一定發(fā)生死鎖。避免死鎖是在系統(tǒng)運(yùn)行過(guò)程中注意避免死鎖的最終發(fā)生。
4.2.1 常用避免死鎖的方法
4.2.1.1 有序資源分配法
這種算法資源按某種規(guī)則系統(tǒng)中的所有資源統(tǒng)一編號(hào)(例如打印機(jī)為1、磁帶機(jī)為2、磁盤(pán)為3、等等),申請(qǐng)時(shí)必須以上升的次序。系統(tǒng)要求申請(qǐng)進(jìn)程:
1、對(duì)它所必須使用的而且屬于同一類的所有資源,必須一次申請(qǐng)完;
2、在申請(qǐng)不同類資源時(shí),必須按各類設(shè)備的編號(hào)依次申請(qǐng)。例如:進(jìn)程PA,使用資源的順序是R1,R2; 進(jìn)程PB,使用資源的順序是R2,R1;若采用動(dòng)態(tài)分配有可能形成環(huán)路條件,造成死鎖。
采用有序資源分配法:R1的編號(hào)為1,R2的編號(hào)為2;
PA:申請(qǐng)次序應(yīng)是:R1,R2
PB:申請(qǐng)次序應(yīng)是:R1,R2
這樣就破壞了環(huán)路條件,避免了死鎖的發(fā)生。
詳見(jiàn)銀行家算法.
避免死鎖的具體實(shí)現(xiàn)通常利用銀行家算法 銀行家算法a、銀行家算法的相關(guān)數(shù)據(jù)結(jié)構(gòu)
1)可利用資源向量Available:用于表示系統(tǒng)里邊各種資源剩余的數(shù)目。由于系統(tǒng)里邊擁有的資源通常都是有很多種(假設(shè)有m種),所以,我們用一個(gè)有m個(gè)元素的數(shù)組來(lái)表示各種資源。數(shù)組元素的初始值為系統(tǒng)里邊所配置的該類全部可用資源的數(shù)目,其數(shù)值隨著該類資源的分配與回收動(dòng)態(tài)地改變。
2) 最大需求矩陣Max:用于表示各個(gè)進(jìn)程對(duì)各種資源的額最大需求量。進(jìn)程可能會(huì)有很多個(gè)(假設(shè)為n個(gè)),那么,我們就可以用一個(gè)nxm的矩陣來(lái)表示各個(gè)進(jìn)程多各種資源的最大需求量
3)分配矩陣Allocation:顧名思義,就是用于表示已經(jīng)分配給各個(gè)進(jìn)程的各種資源的數(shù)目。也是一個(gè)nxm的矩陣。
4)需求矩陣Need:用于表示進(jìn)程仍然需要的資源數(shù)目,用一個(gè)nxm的矩陣表示。
系統(tǒng)可能沒(méi)法一下就滿足了某個(gè)進(jìn)程的最大需求(通常進(jìn)程對(duì)資源的最大需求也是指它在整個(gè)運(yùn)行周期中需要的資源數(shù)目,并不是每一個(gè)時(shí)刻都需要這么多),于是,為了進(jìn)程的執(zhí)行能夠向前推進(jìn),通常,系統(tǒng)會(huì)先分配個(gè)進(jìn)程一部分資源保證進(jìn)程能夠執(zhí)行起來(lái)。那么,進(jìn)程的最大需求減去已經(jīng)分配給進(jìn)程的數(shù)目,就得到了進(jìn)程仍然需要的資源數(shù)目了。
銀行家算法通過(guò)對(duì)進(jìn)程需求、占有和系統(tǒng)擁有資源的實(shí)時(shí)統(tǒng)計(jì),確保系統(tǒng)在分配給進(jìn)程資源不會(huì)造成死鎖才會(huì)給與分配。
注意:死鎖避免的優(yōu)缺點(diǎn)
1)死鎖避免的優(yōu)點(diǎn):不需要死鎖預(yù)防中的搶占和重新運(yùn)行進(jìn)程,并且比死鎖預(yù)防的限制要少。
2)死鎖避免的限制: 必須事先聲明每個(gè)進(jìn)程請(qǐng)求的最大資源量 考慮的進(jìn)程必須無(wú)關(guān)的,也就是說(shuō),它們執(zhí)行的順序必須沒(méi)有任何同步要求的限制 分配的資源數(shù)目必須是固定的。 在占有資源時(shí),進(jìn)程不能退出
4.2.2 常用避免死鎖的技術(shù):
4.2.2.1 加鎖順序:(線程按照一定的順序加鎖)
4.2.2.2 加鎖時(shí)限(線程嘗試獲取鎖的時(shí)候加上一定的時(shí)限,超過(guò)時(shí)限則放棄對(duì)該鎖的請(qǐng)求,并釋放自己占有的鎖)
4.2.2.3 死鎖檢測(cè)
當(dāng)多個(gè)線程需要相同的一些鎖,但是按照不同的順序加鎖,死鎖就很容易發(fā)生。
如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會(huì)發(fā)生??聪旅孢@個(gè)例子:
Thread 1:
lock A
lock B
Thread 2:
wait for A
lock C (when A locked)
Thread 3:
wait for A
wait for B
wait for C
如果一個(gè)線程(比如線程3)需要一些鎖,那么它必須按照確定的順序獲取鎖。它只有獲得了從順序上排在前面的鎖之后,才能獲取后面的鎖。
例如,線程2和線程3只有在獲取了鎖A之后才能嘗試獲取鎖C(譯者注:獲取鎖A是獲取鎖C的必要條件)。因?yàn)榫€程1已經(jīng)擁有了鎖A,所以線程2和3需要一直等到鎖A被釋放。然后在它們嘗試對(duì)B或C加鎖之前,必須成功地對(duì)A加了鎖。
按照順序加鎖是一種有效的死鎖預(yù)防機(jī)制。但是,這種方式需要你事先知道所有可能會(huì)用到的鎖(譯者注:并對(duì)這些鎖做適當(dāng)?shù)呐判?/span>),但總有些時(shí)候是無(wú)法預(yù)知的。
另外一個(gè)可以避免死鎖的方法是在嘗試獲取鎖的時(shí)候加一個(gè)超時(shí)時(shí)間,這也就意味著在嘗試獲取鎖的過(guò)程中若超過(guò)了這個(gè)時(shí)限該線程則放棄對(duì)該鎖請(qǐng)求。若一個(gè)線程沒(méi)有在給定的時(shí)限內(nèi)成功獲得所有需要的鎖,則會(huì)進(jìn)行回退并釋放所有已經(jīng)獲得的鎖,然后等待一段隨機(jī)的時(shí)間再重試。這段隨機(jī)的等待時(shí)間讓其它線程有機(jī)會(huì)嘗試獲取相同的這些鎖,并且讓該應(yīng)用在沒(méi)有獲得鎖的時(shí)候可以繼續(xù)運(yùn)行(譯者注:加鎖超時(shí)后可以先繼續(xù)運(yùn)行干點(diǎn)其它事情,再回頭來(lái)重復(fù)之前加鎖的邏輯)。
以下是一個(gè)例子,展示了兩個(gè)線程以不同的順序嘗試獲取相同的兩個(gè)鎖,在發(fā)生超時(shí)后回退并重試的場(chǎng)景:
Thread 1 locks A
Thread 2 locks B
Thread 1 attempts to lock B but is blocked
Thread 2 attempts to lock A but is blocked
Thread 1's lock attempt on B times out
Thread 1 backs up and releases A as well
Thread 1 waits randomly (e.g. 257 millis) before retrying.
Thread 2's lock attempt on A times out
Thread 2 backs up and releases B as well
Thread 2 waits randomly (e.g. 43 millis) before retrying.
在上面的例子中,線程2比線程1早200毫秒進(jìn)行重試加鎖,因此它可以先成功地獲取到兩個(gè)鎖。這時(shí),線程1嘗試獲取鎖A并且處于等待狀態(tài)。當(dāng)線程2結(jié)束時(shí),線程1也可以順利的獲得這兩個(gè)鎖(除非線程2或者其它線程在線程1成功獲得兩個(gè)鎖之前又獲得其中的一些鎖)。
需要注意的是,由于存在鎖的超時(shí),所以我們不能認(rèn)為這種場(chǎng)景就一定是出現(xiàn)了死鎖。也可能是因?yàn)楂@得了鎖的線程(導(dǎo)致其它線程超時(shí))需要很長(zhǎng)的時(shí)間去完成它的任務(wù)。
此外,如果有非常多的線程同一時(shí)間去競(jìng)爭(zhēng)同一批資源,就算有超時(shí)和回退機(jī)制,還是可能會(huì)導(dǎo)致這些線程重復(fù)地嘗試但卻始終得不到鎖。如果只有兩個(gè)線程,并且重試的超時(shí)時(shí)間設(shè)定為0到500毫秒之間,這種現(xiàn)象可能不會(huì)發(fā)生,但是如果是10個(gè)或20個(gè)線程情況就不同了。因?yàn)檫@些線程等待相等的重試時(shí)間的概率就高的多(或者非常接近以至于會(huì)出現(xiàn)問(wèn)題)。
(譯者注:超時(shí)和重試機(jī)制是為了避免在同一時(shí)間出現(xiàn)的競(jìng)爭(zhēng),但是當(dāng)線程很多時(shí),其中兩個(gè)或多個(gè)線程的超時(shí)時(shí)間一樣或者接近的可能性就會(huì)很大,因此就算出現(xiàn)競(jìng)爭(zhēng)而導(dǎo)致超時(shí)后,由于超時(shí)時(shí)間一樣,它們又會(huì)同時(shí)開(kāi)始重試,導(dǎo)致新一輪的競(jìng)爭(zhēng),帶來(lái)了新的問(wèn)題。)
這種機(jī)制存在一個(gè)問(wèn)題,在Java中不能對(duì)synchronized同步塊設(shè)置超時(shí)時(shí)間。你需要?jiǎng)?chuàng)建一個(gè)自定義鎖,或使用Java5中java.util.concurrent包下的工具。寫(xiě)一個(gè)自定義鎖類不復(fù)雜,但超出了本文的內(nèi)容。后續(xù)的Java并發(fā)系列會(huì)涵蓋自定義鎖的內(nèi)容。
死鎖檢測(cè)是一個(gè)更好的死鎖預(yù)防機(jī)制,它主要是針對(duì)那些不可能實(shí)現(xiàn)按序加鎖并且鎖超時(shí)也不可行的場(chǎng)景。
每當(dāng)一個(gè)線程獲得了鎖,會(huì)在線程和鎖相關(guān)的數(shù)據(jù)結(jié)構(gòu)中(map、graph等等)將其記下。除此之外,每當(dāng)有線程請(qǐng)求鎖,也需要記錄在這個(gè)數(shù)據(jù)結(jié)構(gòu)中。
當(dāng)一個(gè)線程請(qǐng)求鎖失敗時(shí),這個(gè)線程可以遍歷鎖的關(guān)系圖看看是否有死鎖發(fā)生。例如,線程A請(qǐng)求鎖7,但是鎖7這個(gè)時(shí)候被線程B持有,這時(shí)線程A就可以檢查一下線程B是否已經(jīng)請(qǐng)求了線程A當(dāng)前所持有的鎖。如果線程B確實(shí)有這樣的請(qǐng)求,那么就是發(fā)生了死鎖(線程A擁有鎖1,請(qǐng)求鎖7;線程B擁有鎖7,請(qǐng)求鎖1)。
當(dāng)然,死鎖一般要比兩個(gè)線程互相持有對(duì)方的鎖這種情況要復(fù)雜的多。線程A等待線程B,線程B等待線程C,線程C等待線程D,線程D又在等待線程A。線程A為了檢測(cè)死鎖,它需要遞進(jìn)地檢測(cè)所有被B請(qǐng)求的鎖。從線程B所請(qǐng)求的鎖開(kāi)始,線程A找到了線程C,然后又找到了線程D,發(fā)現(xiàn)線程D請(qǐng)求的鎖被線程A自己持有著。這是它就知道發(fā)生了死鎖。
下面是一幅關(guān)于四個(gè)線程(A,B,C和D)之間鎖占有和請(qǐng)求的關(guān)系圖。像這樣的數(shù)據(jù)結(jié)構(gòu)就可以被用來(lái)檢測(cè)死鎖。
4.3 檢測(cè)死鎖
死鎖檢測(cè)算法。
算法一:
算法使用的數(shù)據(jù)結(jié)構(gòu)是如下這些:
占有矩陣A:n*m階,其中n表示并發(fā)進(jìn)程的個(gè)數(shù),m表示系統(tǒng)的各類資源的個(gè)數(shù),這個(gè)矩陣記錄了每一個(gè)進(jìn)程當(dāng)前占有各個(gè)資源類中資源的個(gè)數(shù)。
申請(qǐng)矩陣R:n*m階,其中n表示并發(fā)進(jìn)程的個(gè)數(shù),m表示系統(tǒng)的各類資源的個(gè)數(shù),這個(gè)矩陣記錄了每一個(gè)進(jìn)程當(dāng)前要完成工作需要申請(qǐng)的各個(gè)資源類中資源的個(gè)數(shù)。
空閑向量T:記錄當(dāng)前m個(gè)資源類中空閑資源的個(gè)數(shù)。
完成向量F:布爾型向量值為真(true)或假(false),記錄當(dāng)前n個(gè)并發(fā)進(jìn)程能否進(jìn)行完。為真即能進(jìn)行完,為假則不能進(jìn)行完。
臨時(shí)向量W:開(kāi)始時(shí)W:=T。
算法步驟:
(1)W:=T,
對(duì)于所有的i=1,2,…,n,
如果A[i]=0,則F[i]:=true;否則,F(xiàn)[i]:=false
(2)找滿足下面條件的下標(biāo)i:
F[i]:=false并且R[i]〈=W
如果不存在滿足上面的條件i,則轉(zhuǎn)到步驟(4)。
(3)W:=W+A[i]
F[i]:=true
轉(zhuǎn)到步驟(2)
(4)如果存在i,F(xiàn)[i]:=false,則系統(tǒng)處于死鎖狀態(tài),且Pi進(jìn)程參與了死鎖。什么時(shí)候進(jìn)行死鎖的檢測(cè)取決于死鎖發(fā)生的頻率。如果死鎖發(fā)生的頻率高,那么死鎖檢測(cè)的頻率也要相應(yīng)提高,這樣一方面可以提高系統(tǒng)資源的利用率,一方面可以避免更多的進(jìn)程卷入死鎖。如果進(jìn)程申請(qǐng)資源不能滿足就立刻進(jìn)行檢測(cè),那么每當(dāng)死鎖形成時(shí)即能被發(fā)現(xiàn),這和死鎖避免的算法相近,只是系統(tǒng)的開(kāi)銷較大。為了減小死鎖檢測(cè)帶來(lái)的系統(tǒng)開(kāi)銷,一般采取每隔一段時(shí)間進(jìn)行一次死鎖檢測(cè),或者在CPU的利用率降低到某一數(shù)值時(shí),進(jìn)行死鎖的檢測(cè)。
那么當(dāng)檢測(cè)出死鎖時(shí),這些線程該做些什么呢?
1)一個(gè)可行的做法是釋放所有鎖,回退,并且等待一段隨機(jī)的時(shí)間后重試。這個(gè)和簡(jiǎn)單的加鎖超時(shí)類似,不一樣的是只有死鎖已經(jīng)發(fā)生了才回退,而不會(huì)是因?yàn)榧渔i的請(qǐng)求超時(shí)了。雖然有回退和等待,但是如果有大量的線程競(jìng)爭(zhēng)同一批鎖,它們還是會(huì)重復(fù)地死鎖(編者注:原因同超時(shí)類似,不能從根本上減輕競(jìng)爭(zhēng))。
2)一個(gè)更好的方案是給這些線程設(shè)置優(yōu)先級(jí),讓一個(gè)(或幾個(gè))線程回退,剩下的線程就像沒(méi)發(fā)生死鎖一樣繼續(xù)保持著它們需要的鎖。如果賦予這些線程的優(yōu)先級(jí)是固定不變的,同一批線程總是會(huì)擁有更高的優(yōu)先級(jí)。為避免這個(gè)問(wèn)題,可以在死鎖發(fā)生的時(shí)候設(shè)置隨機(jī)的優(yōu)先級(jí)。
算法二:
死鎖檢測(cè)與解除 ----- 在檢測(cè)到運(yùn)行系統(tǒng)進(jìn)入死鎖,進(jìn)行恢復(fù)。 允許系統(tǒng)進(jìn)入到死鎖狀態(tài) 死鎖檢測(cè)下圖截自《操作系統(tǒng)--精髓與設(shè)計(jì)原理》
4.4 死鎖的解除
如果利用死鎖檢測(cè)算法檢測(cè)出系統(tǒng)已經(jīng)出現(xiàn)了死鎖 ,那么,此時(shí)就需要對(duì)系統(tǒng)采取相應(yīng)的措施。
常用的解除死鎖的方法:
1、搶占資源:從一個(gè)或多個(gè)進(jìn)程中搶占足夠數(shù)量的資源分配給死鎖進(jìn)程,以解除死鎖狀態(tài)。但應(yīng)防止被掛起的進(jìn)程長(zhǎng)時(shí)間得不到資源,而處于資源匱乏的狀態(tài)。
2、終止(或撤銷)進(jìn)程:終止或撤銷系統(tǒng)中的一個(gè)或多個(gè)死鎖進(jìn)程,直至打破死鎖狀態(tài)。撤銷的原則可以按進(jìn)程優(yōu)先級(jí)和撤銷進(jìn)程代價(jià)的高低進(jìn)行。
a、終止所有的死鎖進(jìn)程。這種方式簡(jiǎn)單粗暴,但是代價(jià)很大,很有可能會(huì)導(dǎo)致一些已經(jīng)運(yùn)行了很久的進(jìn)程前功盡棄。
b、逐個(gè)終止進(jìn)程,直至死鎖狀態(tài)解除。該方法的代價(jià)也很大,因?yàn)槊拷K止一個(gè)進(jìn)程就需要使用死鎖檢測(cè)來(lái)檢測(cè)系統(tǒng)當(dāng)前是否處于死鎖狀態(tài)。要求系統(tǒng)保持進(jìn)程的歷史信息,設(shè)置還原點(diǎn)。另外,每次終止進(jìn)程的時(shí)候終止那個(gè)進(jìn)程呢?每次都應(yīng)該采用最優(yōu)策略來(lái)選擇一個(gè)“代價(jià)最小”的進(jìn)程來(lái)解除死鎖狀態(tài)。
一般根據(jù)如下幾個(gè)方面來(lái)決定終止哪個(gè)進(jìn)程: 1】進(jìn)程的優(yōu)先級(jí)
2】進(jìn)程已運(yùn)行時(shí)間以及運(yùn)行完成還需要的時(shí)間
3】進(jìn)程已占用系統(tǒng)資源
4】進(jìn)程運(yùn)行完成還需要的資源終止進(jìn)程數(shù)目
5】進(jìn)程是交互還是批處理
十一、MySQL支持的事務(wù)隔離級(jí)別
4種:
read uncommited –> 讀”臟”數(shù)據(jù)現(xiàn)象,“臟讀"
read commited –> 不可重復(fù)讀現(xiàn)象,
repeatable read(MySQL默認(rèn))–>幻讀現(xiàn)象
serializable->可串行化,
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。