在java開發(fā)中,避免不了要加鎖控制程序邏輯,但加鎖有可能導致死鎖,造成線程永遠卡死在等待釋放鎖,后面的代碼得不到執(zhí)行;
在java里,一般是通過synchronized關鍵字加鎖,在jdk1.5版本中新增了Lock接口顯示的加鎖,本文討論用這兩種方式實現(xiàn)死鎖;
方式一:
public static void main(String[] args) { Object lock1 = new Object(); Object lock2 = new Object(); ExecutorService service = Executors.newCachedThreadPool(); service.execute(() ->{ while (true){ synchronized (lock2){ synchronized (lock1){ System.out.println("Thread1"); } } } }); service.execute(() ->{ while (true){ synchronized (lock1){ synchronized (lock2){ System.out.println("Thread2"); } } } }); service.shutdown(); }
上面的代碼開啟了兩個線程,兩個線程都循環(huán)打印字符串,正常來說兩個線程都會不斷打印,但這里出現(xiàn)了死鎖,導致兩個線程都會打印停止,邏輯分析:兩個線程執(zhí)行到某一時刻,線程一執(zhí)行到synchronized (lock2){這一句,獲得了對象lock2上的鎖,線程二執(zhí)行到synchronized (lock1){這一句,獲得了對象lock1上的鎖,接下來,線程一需要執(zhí)行synchronized (lock1){這一句嘗試獲取lock1的鎖,由于lock1的鎖被線程二占用,線程一等待線程二釋放lock1的鎖,這時線程二需要執(zhí)行synchronized (lock2){這一句,嘗試獲取lock2上的鎖,由于lock2上的鎖被線程一占用,線程二等待線程一釋放lock2上的鎖,這樣,兩個線程互相等待釋放鎖,導致兩個線程永遠無法執(zhí)行后面的代碼,導致死鎖;
方式二:
static final Lock LOCK1 = new ReentrantLock(); static final Lock LOCK2 = new ReentrantLock(); public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); service.execute(new PrintTask1(1)); service.execute(new PrintTask2(2)); } static class PrintTask1 implements Runnable{ private int id = -1; public PrintTask1(int i){ id = i; } @Override public void run() { while (true){ LOCK1.lock(); LOCK2.lock(); System.out.println("" + id); LOCK2.unlock(); LOCK1.unlock(); } } } static class PrintTask2 implements Runnable{ private int id = -1; public PrintTask2(int i){ id = i; } @Override public void run() { while (true){ LOCK2.lock(); LOCK1.lock(); System.out.println("" + id); LOCK2.unlock(); LOCK1.unlock(); } } }
方式二的代碼屬于顯示加鎖,使用了jdk1.5新增的Lock接口,與方式一類似,當兩個線程執(zhí)行到某一時刻,線程一執(zhí)行了LOCK1.lock()獲得了LOCK1鎖,線程二執(zhí)行了LOCK2.lock()獲得了LOCK2鎖,接下來兩個線程又要互相等待對方釋放鎖,導致死鎖。