有一天,小李去參加面試,面試官讓他寫一個死鎖的程序,小李思索了一兩分鐘,於是開始了死鎖的代碼:
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (Integer.class) {
System.out.println("t1 Integer");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Long.class) {
System.out.println("t1 Long");
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
synchronized (Long.class) {
System.out.println("t2 Long");
synchronized (Integer.class) {
System.out.println("t2 Integer");
}
}
});
t2.start();
}
面試官一看,小夥子可以的;面試關就繼續問小夥子,小李啊,如果t2獲取不到時間片,還會死鎖麼?
小李一想,確實如此,能不能改下代碼,一定能讓這段代碼產生死鎖呢?於是小李,又開始了思索,沒過一會呢,小李靈機一動,加了一行代碼,小李說,這樣就一定會產生死鎖了,請看:
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (Integer.class) {
System.out.println("t1 Integer");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Long.class) {
System.out.println("t1 Long");
}
}
});
t1.setDaemon(true);
t1.start();
Thread t2 = new Thread(() -> {
synchronized (Long.class) {
System.out.println("t2 Long");
synchronized (Integer.class) {
System.out.println("t2 Integer");
}
}
});
t2.start();
}
看出有什麼不同了麼?不錯,就是把線程1設置爲守護線程,這樣,只要線程2不運行,守護線程就不會結束,又因爲兩個線程相互持有對象,就會導致程序一直結束不了,從而造成死鎖。
守護線程(即daemon thread),是個服務線程,準確地來說就是服務其他的線程,這是它的作用——而其他的線程只有一種,那就是用戶線程。所以java裏線程分2種,
1、守護線程,比如垃圾回收線程,就是最典型的守護線程。
2、用戶線程,就是應用程序裏的自定義線程。
守護線程,專門用於服務其他的線程,如果其他的線程(即用戶自定義線程)都執行完畢,連main線程也執行完畢,那麼jvm就會退出(即停止運行)——此時,連jvm都停止運行了,守護線程當然也就停止執行了。