JAVA多線程(四)

死鎖
1、何爲死鎖
  簡單說來,死鎖就是系統中的線程因相互等侍彼此佔有的資源而暫停執行,造成系統假死的現象。

2、死鎖是如何發生的
  假設系統有兩個互斥資源A和B,系統中的兩個線程1和2都要獲得A和B之後才能正常工作, 但是線程1先取資源A再取資源B,線程2先取資源B再取資源A。這樣就有可能發生這樣的情況:線程1先申請了資源A,再準備申請資源B的時候,由於處理器調度,線程2開始執行;線程2申請了資源B,在準備取資源A的時候發現資源A已經被別的線程(1)佔用了,這樣線程2暫停執行;當線程1接着執行時,發現資源B已經被別的線程(2)佔用了。就這樣,線程1和2相互等待彼此佔有的資源而一直停止下去,造成了系統假死的現象。

3、一個死鎖的例子
  下面這個例子簡單了模擬了上面的死鎖現象:
public class Resource
{
    private boolean isTaken = false;
    
    public synchronized void take() throws InterruptedException
    {
        while (isTaken)
            wait();
        
        isTaken = true;
    }
    
    public synchronized void drop()
    {
        isTaken = false;
        
        notifyAll();
    }
    
    public static void main(String[] args) throws IOException
    {
        Resource A = new Resource();
        Resource B = new Resource();
        
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new MyThread(A, B));
        exec.execute(new MyThread(B, A));
        
        System.in.read();
        
        exec.shutdownNow();
    }
}

class MyThread implements Runnable
{
    private static int counter = 0;
    private final int id = counter++;
    
    private Resource first, second;
    
    public MyThread(Resource first, Resource second)
    {
        this.first = first;
        this.second = second;
    }
    
    @Override
    public void run()
    {
        try
        {
            while (!Thread.interrupted())
            {
                first.take();
                Thread.yield();
                second.take();

                System.out.println("Thread " + id + " working...");
                TimeUnit.SECONDS.sleep(1);

                first.drop();
                second.drop();
                Thread.yield();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

4、死鎖發生的必要條件
  死鎖的發生需要滿足四個必要條件:
  • 互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程佔用。如果此時還有其它進程請求資源,則請求者只能等待,直至佔有資源的進程用畢釋放。
  • 請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程佔有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。
  • 不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。
  • 循環等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。

5、解決死鎖
  死鎖只有在四個必要條件都滿足的情況下才可能發生,因此,只需要破壞其中一個就可以防止死鎖的發生了。

  上面的例子中,最容易破壞的就是第四個循環等待條件了,只要讓線程2也像線程1那樣,先申請資源A,再申請資源B就破壞了第四個條件,也就可以防止死鎖的產生了。爲止,只需要簡單修改上例的main()方法即可:
public static void main(String[] args) throws IOException
{
    Resource A = new Resource();
    Resource B = new Resource();
        
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.execute(new MyThread(A, B));
    exec.execute(new MyThread(A, B));
        
    System.in.read();
        
    exec.shutdownNow();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章