Java多線程死鎖與資源限制操作
鎖是個非常有用的工具,運用場景非常多,因為它使用起來非常簡單,而且易于理解。但同時它也會帶來一些困擾,那就是可能會引起死鎖,一旦產(chǎn)生死鎖,就會造成系統(tǒng)功能不可用。
死鎖的概念
那什么是死鎖呢?所謂死鎖: 是指兩個或兩個以上的進程在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進下去。此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
死鎖產(chǎn)生的必要條件
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內(nèi)某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。
2)請求和保持條件:指進程已經(jīng)保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
4)環(huán)路等待條件:指在發(fā)生死鎖時,必然存在一個進程——資源的環(huán)形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。
死鎖代碼實例
public class DeadLockDemo { private static String A = 'A'; private static String B = 'B'; public static void main(String[] args) { new DeadLockDemo().deadLock(); } /** * 死鎖 * @author fuyuwei * 2017年5月13日 下午9:27:32 */ private void deadLock() { Thread t1 = new Thread(new Runnable() { @SuppressWarnings('static-access') @Override public void run() {synchronized (A) { try { Thread.currentThread().sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (B) { System.out.println('1'); }} } }); Thread t2 = new Thread(new Runnable() { @Override public void run() {synchronized (B) { synchronized (A) { System.out.println('2'); }} } }); t1.start(); t2.start(); }}
線程A睡眠2秒之后鎖定B同步打印1,但是這時候B已經(jīng)被第二個線程鎖定,并且第二天線程又鎖定A打印2,就這樣A等待B但是握著B不放,B等待A但是握著A不放,就產(chǎn)生了死鎖。
當然這段代碼純粹是為了演示死鎖,在實際工作中基本上不會出現(xiàn)這種代碼。在實際工作中線程可能拿到一個數(shù)據(jù)庫鎖,釋放鎖的時候拋出了異常,沒釋放掉。
一旦出現(xiàn)死鎖,業(yè)務(wù)是可感知的,因為不能繼續(xù)提供服務(wù)了,那么只能通過dump線程查看到底是哪個線程出現(xiàn)了問題,以下線程信息告訴我們是DeadLockDemo類的第42行和第31行引起的死鎖。
'Thread-2' prio=5 tid=7fc0458d1000 nid=0x116c1c000 waiting for monitor entry [116c1b000java.lang.Thread.State: BLOCKED (on object monitor)at com.ifeve.book.forkjoin.DeadLockDemo$2.run(DeadLockDemo.java:42)- waiting to lock <7fb2f3ec0> (a java.lang.String)- locked <7fb2f3ef8> (a java.lang.String)at java.lang.Thread.run(Thread.java:695)'Thread-1' prio=5 tid=7fc0430f6800 nid=0x116b19000 waiting for monitor entry [116b18000java.lang.Thread.State: BLOCKED (on object monitor)at com.ifeve.book.forkjoin.DeadLockDemo$1.run(DeadLockDemo.java:31)- waiting to lock <7fb2f3ef8> (a java.lang.String)- locked <7fb2f3ec0> (a java.lang.String)at java.lang.Thread.run(Thread.j
避免死鎖的方法
1、避免一個線程同時獲取多個鎖。
2、避免一個線程在鎖內(nèi)同時占用多個資源,盡量保證每個鎖只占用一個資源。
3、嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內(nèi)部鎖機制。
4、對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個數(shù)據(jù)庫連接里,否則會出現(xiàn)解鎖失敗的情況。
什么是資源限制
資源限制是指在進行并發(fā)編程時,程序的執(zhí)行速度受限于計算機硬件資源或軟件資源。例如,服務(wù)器的帶寬只有2Mb/s,某個資源的下載速度是1Mb/s每秒,系統(tǒng)啟動10個線程下載資源,下載速度不會變成10Mb/s,所以在進行并發(fā)編程時,要考慮這些資源的限制。硬件資源限制有帶寬的上傳/下載速度、硬盤讀寫速度和CPU的處理速度。軟件資源限制有數(shù)據(jù)庫的連接數(shù)和socket連接數(shù)等。
資源限制引發(fā)的問題
在并發(fā)編程中,將代碼執(zhí)行速度加快的原則是將代碼中串行執(zhí)行的部分變成并發(fā)執(zhí)行,但是如果將某段串行的代碼并發(fā)執(zhí)行,因為受限于資源,仍然在串行執(zhí)行,這時候程序不僅不會加快執(zhí)行,反而會更慢,因為增加了上下文切換和資源調(diào)度的時間。例如,之前看到一段程序使用多線程在辦公網(wǎng)并發(fā)地下載和處理數(shù)據(jù)時,導致CPU利用率達到100%,幾個小時都不能運行完成任務(wù),后來修改成單線程,一個小時就執(zhí)行完成了。
如何解決資源限制的問題
對于硬件資源限制,可以考慮使用集群并行執(zhí)行程序。既然單機的資源有限制,那么就讓程序在多機上運行。比如使用ODPS、Hadoop或者自己搭建服務(wù)器集群,不同的機器處理不同的數(shù)據(jù)。可以通過“數(shù)據(jù)ID%機器數(shù)”,計算得到一個機器編號,然后由對應(yīng)編號的機器處理這筆數(shù)據(jù)。對于軟件資源限制,可以考慮使用資源池將資源復用。比如使用連接池將數(shù)據(jù)庫和Socket連接復用,或者在調(diào)用對方webservice接口獲取數(shù)據(jù)時,只建立一個連接。
在資源限制情況下進行并發(fā)編程
如何在資源限制的情況下,讓程序執(zhí)行得更快呢?方法就是,根據(jù)不同的資源限制調(diào)整程序的并發(fā)度,比如下載文件程序依賴于兩個資源——帶寬和硬盤讀寫速度。有數(shù)據(jù)庫操作時,涉及數(shù)據(jù)庫連接數(shù),如果SQL語句執(zhí)行非常快,而線程的數(shù)量比數(shù)據(jù)庫連接數(shù)大很多,則某些線程會被阻塞,等待數(shù)據(jù)庫連接。
補充知識:初入Java并發(fā)-避免死鎖的常見方法
1、避免一個線程同時獲取多個鎖
2、避免一個線程在鎖內(nèi)同時占用多個資源,盡量保證每個鎖只占用一個資源
3、嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用內(nèi)部鎖機制
4、對于數(shù)據(jù)庫鎖,加鎖和解鎖必須在一個數(shù)據(jù)庫連接里,否則會出現(xiàn)解鎖失敗的情況。
以上這篇Java多線程死鎖與資源限制操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。
相關(guān)文章:
