java多線程中死結情況的一個樣本,java多線程樣本
下面是死結情況的一個範例程式碼
package com.qust.demo.money;class A {public synchronized void foo(B b) {System.out.println(Thread.currentThread().getName() + " 進入A的foo");try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 試圖調用B的last");b.last();}public synchronized void last() {System.out.println("A的last()");}}class B {public synchronized void bar(A a) {System.out.println(Thread.currentThread().getName() + " 進入B的bar");try {Thread.sleep(200);} catch (InterruptedException ex) {ex.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 試圖調用A的last");a.last();}public synchronized void last() {System.out.println("B的last()");}}public class DeadLock implements Runnable {A a = new A();B b = new B();public void init() {Thread.currentThread().setName("主線程");System.out.println("進入主線程");a.foo(b);}public void run() {Thread.currentThread().setName("副線程");System.out.println("進入副線程");b.bar(a);}public static void main(String[] args) {DeadLock dl = new DeadLock();new Thread(dl).start();dl.init();}}
下面是運行結果
進入主線程進入副線程主線程 進入A的foo副線程 進入B的bar副線程 試圖調用A的last主線程 試圖調用B的last
我們看到,正常情況下,最後還應該列印出“A的last()”和"B的last()",但是因為線程死結的原因,所以程式一直在等待執行,沒能正常的執行下去。
在上面的代碼裡面,為什麼會出現死結這種情況呢?我們來簡單分析一下。
首先雖然副線程的start()是在主線程的init()之前,但是因為多線程開啟也需要一段時間,所以我們可以看到,是主線程的init()方法執行在前,然後在init()裡面調用了a.foo()。A類和B類中的方法都是同步方法,因此,A的對象和B的對象都是同步鎖。在進入A的foo()之前,線程會對A加鎖,然後線程睡眠200ms,這時候副線程調用B的bar(),同樣的會對B加鎖,然後睡眠200ms。這時候,主線程喚醒,試圖調用B的bar方法,因為是同步方法,所以需要對B加鎖,但是這時候B已經被副線程鎖住了,所以主線程就一直處於阻塞狀態。當副線程喚醒的時候,試圖調用A的同步方法,同樣需要對A加鎖,但是這時候主線程持有A的鎖,並處於阻塞狀態,所以副線程也不能向下執行,大家都在等著對方釋放鎖,因此出現了我們上面說的死結的情況。
如果我們把A和B的last()的synchronized去掉,那麼程式在調用last()之前就不需要對對象進行加鎖,也就不會出現死結的情況。
因為在工作中還沒有遇到這種情況,所以只能拿這個執行個體程式講解了。