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();
}