Java多執行緒(五)執行緒死鎖
阿新 • • 發佈:2021-07-10
死鎖
1.死鎖的理解:不同的執行緒分別佔用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖
2.說明:
1)出現死鎖後,不會出現異常,不會出現提示,只是所有的執行緒都處於阻塞狀態,無法繼續
2)我們使用同步時,要避免出現死鎖。
1 public class ThreadTest { 2 3 public static void main(String[] args) { 4 5 StringBuffer s1 = new StringBuffer(); 6 StringBuffer s2 = new StringBuffer();7 8 9 new Thread(){ 10 @Override 11 public void run() { 12 13 synchronized (s1){ 14 15 s1.append("a"); 16 s2.append("1"); 17 18 try { 19 Thread.sleep(100); 20 } catch(InterruptedException e) { 21 e.printStackTrace(); 22 } 23 24 synchronized (s2){ 25 s1.append("b"); 26 s2.append("2"); 27 28 System.out.println(s1); 29 System.out.println(s2);30 } 31 } 32 } 33 }.start(); 34 35 36 new Thread(new Runnable() { 37 @Override 38 public void run() { 39 synchronized (s2){ 40 41 s1.append("c"); 42 s2.append("3"); 43 44 try { 45 Thread.sleep(100); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 50 synchronized (s1){ 51 s1.append("d"); 52 s2.append("4"); 53 54 System.out.println(s1); 55 System.out.println(s2); 56 } 57 } 58 } 59 }).start(); 60 } 61 }
死鎖的演示
1 package com.atguigu.java1; 2 //死鎖的演示 3 class A { 4 public synchronized void foo(B b) { //同步監視器:A類的物件:a 5 System.out.println("當前執行緒名: " + Thread.currentThread().getName() 6 + " 進入了A例項的foo方法"); // ① 7 // try { 8 // Thread.sleep(200); 9 // } catch (InterruptedException ex) { 10 // ex.printStackTrace(); 11 // } 12 System.out.println("當前執行緒名: " + Thread.currentThread().getName() 13 + " 企圖呼叫B例項的last方法"); // ③ 14 b.last(); 15 } 16 17 public synchronized void last() {//同步監視器:A類的物件:a 18 System.out.println("進入了A類的last方法內部"); 19 } 20 } 21 22 class B { 23 public synchronized void bar(A a) {//同步監視器:b 24 System.out.println("當前執行緒名: " + Thread.currentThread().getName() 25 + " 進入了B例項的bar方法"); // ② 26 // try { 27 // Thread.sleep(200); 28 // } catch (InterruptedException ex) { 29 // ex.printStackTrace(); 30 // } 31 System.out.println("當前執行緒名: " + Thread.currentThread().getName() 32 + " 企圖呼叫A例項的last方法"); // ④ 33 a.last(); 34 } 35 36 public synchronized void last() {//同步監視器:b 37 System.out.println("進入了B類的last方法內部"); 38 } 39 } 40 41 public class DeadLock implements Runnable { 42 A a = new A(); 43 B b = new B(); 44 45 public void init() { 46 Thread.currentThread().setName("主執行緒"); 47 // 呼叫a物件的foo方法 48 a.foo(b); 49 System.out.println("進入了主執行緒之後"); 50 } 51 52 public void run() { 53 Thread.currentThread().setName("副執行緒"); 54 // 呼叫b物件的bar方法 55 b.bar(a); 56 System.out.println("進入了副執行緒之後"); 57 } 58 59 public static void main(String[] args) { 60 DeadLock dl = new DeadLock(); 61 new Thread(dl).start(); 62 63 64 dl.init(); 65 } 66 }
Lock(鎖)
- 從JDK5.0開始,Java提供了更強大的執行緒同步機制——通過顯示定義同步鎖物件來實現同步。同步鎖使用Lock物件充當。
- java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具。鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件。
- ReentrantLock類實現了Lock,它擁有與synchronized相同的併發性和記憶體語義,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖。
1 package com.atguigu.java1; 2 3 import java.util.concurrent.locks.ReentrantLock; 4 5 /** 6 * 解決執行緒安全問題的方式三:Lock鎖 --- JDK5.0新增 7 * 8 * 1. 面試題:synchronized 與 Lock的異同? 9 * 相同:二者都可以解決執行緒安全問題 10 * 不同:synchronized機制在執行完相應的同步程式碼以後,自動的釋放同步監視器 11 * Lock需要手動的啟動同步(lock()),同時結束同步也需要手動的實現(unlock()) 12 * 13 * 2.優先使用順序: 14 * Lock 同步程式碼塊(已經進入了方法體,分配了相應資源) 同步方法(在方法體之外) 15 * 16 * 17 * 面試題:如何解決執行緒安全問題?有幾種方式 18 */ 19 class Window implements Runnable{ 20 21 private int ticket = 100; 22 //1.例項化ReentrantLock 23 private ReentrantLock lock = new ReentrantLock(); 24 25 @Override 26 public void run() { 27 while(true){ 28 try{ 29 30 //2.呼叫鎖定方法lock() 31 lock.lock(); 32 33 if(ticket > 0){ 34 35 try { 36 Thread.sleep(100); 37 } catch (InterruptedException e) { 38 e.printStackTrace(); 39 } 40 41 System.out.println(Thread.currentThread().getName() + ":售票,票號為:" + ticket); 42 ticket--; 43 }else{ 44 break; 45 } 46 }finally { 47 //3.呼叫解鎖方法:unlock() 48 lock.unlock(); 49 } 50 51 } 52 } 53 } 54 55 public class LockTest { 56 public static void main(String[] args) { 57 Window w = new Window(); 58 59 Thread t1 = new Thread(w); 60 Thread t2 = new Thread(w); 61 Thread t3 = new Thread(w); 62 63 t1.setName("視窗1"); 64 t2.setName("視窗2"); 65 t3.setName("視窗3"); 66 67 t1.start(); 68 t2.start(); 69 t3.start(); 70 } 71 }