Java 多線程編程之七:死結(附原始碼)

來源:互聯網
上載者:User

Java 多線程編程之七:死結(附原始碼)

原始碼下載
        多線程編程中,線程死結也是一個比較有趣的問題。然而死結發生的可能性很小,正因如此,大家可能對此不是很熟悉。但是死結並不是不重要,因為它確確實實存在著,隨時會出現在我們的程式之中。很多朋友面試的時候都遇到過這樣類似的一個編程題:使用 Java 寫一個死結。問題不是很難,但是由於大家現實中處理的比較少,還真難住了一部分人。本文列舉了一個簡單但又不失為經典的死結的原始碼,並解釋了死結發生的原因。相信讀者看過之後對死結會有更進一步的認識!
        死結發生的原因一般是兩個對象的鎖相互等待造成的。比如線程 1 先拿到了對象 A 的對象鎖,線程 2 先拿到了對象 B 的對象鎖。線程 1 這時候要求拿到對象 B 的對象鎖,因為對象 B 的對象鎖暫時被線程 2 所持有,所以線程 1 只能抱著對象 A 的對象鎖等待線程 2 釋放掉對象 B 的對象鎖。而如果這個時候,持有對象 B 的對象鎖的線程 2 要求訪問對象 A 的對象鎖,因為對象 A 的對象鎖暫時被線程 1 所持有,所以線程 2 只能抱著對象 B 的對象鎖等待線程 1 釋放掉對象 A 的對象鎖。這個時候兩個線程就陷入了互相等待的僵局之中。程式就進入了死結的狀態,除非你手工停掉這個程式,否則它將永久地僵持下去。
        是不是有點繞?嘿嘿,如果你弄明白了對象鎖的概念,死結的原因也就一目瞭然。關於對象鎖,作者在上一篇部落格《Java 多線程編程之六:線程之間的通訊(附原始碼)》中有所介紹,這裡就不再贅述了。
        用代碼說明問題,直接看代碼示範。
死結例子-資源原始碼

package com.defonds.deadlock;<br />/**<br /> *<br /> *<br /> * 項目名稱:ThreadDeadLock<br /> * 類名稱:Resource<br /> * 類描述:資源類,用於代表線程競爭的資料資源<br /> * 建立人:Defonds<br /> * 建立時間:2010-1-26 下午02:01:16<br /> * 修改人:Defonds<br /> * 修改時間:2010-1-26 下午02:01:16<br /> * 修改備忘:<br /> * @version<br /> *<br /> */<br />public class Resource {</p><p>private int value;//資源的屬性<br />public int getValue() {<br />return value;<br />}<br />public void setValue(int value) {<br />this.value = value;<br />}<br />}<br />
死結例子-資源管理員原始碼

package com.defonds.deadlock;<br />/**<br /> *<br /> *<br /> * 項目名稱:ThreadDeadLock<br /> * 類名稱:ResourceManager<br /> * 類描述:資源管理類,對資源資料操作的介面<br /> * 建立人:Defonds<br /> * 建立時間:2010-1-26 下午02:04:59<br /> * 修改人:Defonds<br /> * 修改時間:2010-1-26 下午02:04:59<br /> * 修改備忘:<br /> * @version<br /> *<br /> */<br />public class ResourceManager {</p><p>/**<br /> * 管理的兩個資源<br /> */<br />private Resource resourceA = new Resource();<br />private Resource resourceB = new Resource();</p><p>/**<br /> * 建立一個新的執行個體 ResourceManager.<br /> */<br />public ResourceManager(){<br />this.resourceA.setValue(0);<br />this.resourceB.setValue(0);<br />}</p><p>/**<br /> * 資源的讀取<br /> */<br />public int read(){<br />synchronized(this.resourceA){<br />System.out.println(Thread.currentThread().getName() + "線程拿到了資源 resourceA 的對象鎖");<br />synchronized(resourceB){<br />System.out.println(Thread.currentThread().getName() + "線程拿到了資源 resourceB 的對象鎖");<br />return this.resourceA.getValue() + this.resourceB.getValue();<br />}<br />}<br />}</p><p>/**<br /> * 資源的改寫<br /> */<br />public void write(int a,int b){<br />synchronized(this.resourceB){<br />System.out.println(Thread.currentThread().getName() + "線程拿到了資源 resourceB 的對象鎖");<br />synchronized(this.resourceA){<br />System.out.println(Thread.currentThread().getName() + "線程拿到了資源 resourceA 的對象鎖");<br />this.resourceA.setValue(a);<br />this.resourceB.setValue(b);<br />}<br />}<br />}<br />}<br />
死結例子-自訂線程原始碼

package com.defonds.deadlock;<br />/**<br /> *<br /> *<br /> * 項目名稱:ThreadDeadLock<br /> * 類名稱:CustomizeThread<br /> * 類描述:自訂線程類<br /> * 建立人:Defonds<br /> * 建立時間:2010-1-26 下午02:29:01<br /> * 修改人:Defonds<br /> * 修改時間:2010-1-26 下午02:29:01<br /> * 修改備忘:<br /> * @version<br /> *<br /> */<br />public class CustomizeThread extends Thread {<br />private ResourceManager resourceManger;//資源管理類的私人引用,通過此引用可以通過其相關介面對資源進行讀寫<br />private int a,b;//將要寫入資源的資料</p><p>/**<br /> *<br /> * 建立一個新的執行個體 CustomizeThread.<br /> * @param resourceManger<br /> * @param a<br /> * @param b<br /> */<br />public CustomizeThread(ResourceManager resourceManger,int a,int b){<br />this.resourceManger = resourceManger;<br />this.a = a;<br />this.b = b;<br />}</p><p>/**<br /> * 重寫 java.lang.Thread 的 run 方法<br /> */<br />public void run(){<br />/**<br /> * 為了示範死結的出現,這裡對資源進行反覆讀寫<br /> * 實際業務中可能唯讀寫一次<br /> */<br />while(true){<br />this.resourceManger.read();<br />this.resourceManger.write(this.a, this.b);<br />}<br />}<br />}<br />
死結例子-程式入口原始碼

package com.defonds.deadlock;<br />/**<br /> *<br /> *<br /> * 項目名稱:ThreadDeadLock<br /> * 類名稱:DeadLockApp<br /> * 類描述:死結程式入口<br /> * 建立人:Defonds<br /> * 建立時間:2010-1-26 下午02:37:27<br /> * 修改人:Defonds<br /> * 修改時間:2010-1-26 下午02:37:27<br /> * 修改備忘:<br /> * @version<br /> *<br /> */<br />public class DeadLockApp {<br />public static void main(String[] args) {</p><p>/**<br /> * 死結示範線程初始化<br /> */<br />ResourceManager resourceManager = new ResourceManager();<br />CustomizeThread customizedThread0 = new CustomizeThread(resourceManager,1,2);<br />CustomizeThread customizedThread1 = new CustomizeThread(resourceManager,2,4);</p><p>/**<br /> * 死結示範線程啟動<br /> */<br />customizedThread0.start();<br />customizedThread1.start();<br />}<br />}<br />
        下面一次執行本代碼時死結發生的控制台輸出片段:
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceA 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-0線程拿到了資源 resourceB 的對象鎖
Thread-1線程拿到了資源 resourceA 的對象鎖
        由上述例子可以看出,一旦兩個線程互相等待的局面出現,死結現象就不可避免地發生了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.