1、實體類
1 package com.cn.donleo.thread.phone; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 /** 7 * @author liangd 8 * date 2020-11-02 08:53 9 * code 10 */ 11 public class MyPhone implements Runnable { 12 13 private int phoneNum = 100; 14 /* 15 * 同步鎖的兩種方式: 16 * 1、鎖對象 synchronized (this) 17 * 2、鎖方法 public synchronized void run() 鎖方法其實是鎖的當前對象,synchronized (this) ,synchronized (MyPhone.class) 18 * 同步鎖優缺點: 19 * 1、解決了多線程安全問題 20 * 2、增加了線程同步鎖后,必須一個一個執行,浪費資源,降低了程序的運行效率 21 */ 22 /** 23 * 1、對不同的runnable傳入同一個lock 24 * 2、lock和synchronized類似,只不過這個是手動上鎖,手動釋放鎖 25 * synchronized屬於自動上鎖和釋放 26 * 3、Lock和synchronized的區別 27 * 1)synchronized試用於並發量小的,lock更適用於並發量大的 28 * 2)lock更靈活,可以自由定義多把鎖的枷鎖解鎖順序 29 * 3)Lock還有中斷鎖和定時鎖。 30 * 4)當線程運行到synchronized同步方法中,就會擁有obj對象的對象鎖 31 * 5)Wait會釋放對象鎖,sleep不會釋放對象鎖 32 * 6)Synchronized用對象鎖: 33 */ 34 private Lock lock = new ReentrantLock(); 35 36 @Override 37 public synchronized void run() { 38 //while(true)是一個無窮循環語句 我們必須在他的循環語句內部加入一個判斷 當他達到了什么要求就會跳出 39 while (true) { 40 //鎖對象,指當前對象 41 // synchronized (this){ 42 // synchronized ("Myphone"){ 43 if (phoneNum > 0) { 44 lock.lock(); 45 try { 46 /* 47 1、賣手機的時候不可能都是同時賣出或者被預定在MyPhone類里面增加sleep 48 休眠100毫秒 49 2、淘寶最后一部手機進來 50 京東也有人預定手機,現在沒有--,也可以進來 51 同理,拼多多也有人預定手機,現在沒有--,也可以進來 52 3、會出現負數 53 */ 54 Thread.sleep(100); 55 } catch (InterruptedException e) { 56 e.printStackTrace(); 57 } finally { 58 //lock.unlock()要放在finally里面,因為如果上面報異常的情況下鎖就不會手動釋放,放在finally里面始終要執行 59 lock.unlock(); 60 } 61 System.out.println(Thread.currentThread().getName() + "賣出了一部手機,還剩余" + --phoneNum + "部手機"); 62 } else { 63 System.out.println("手機已經全部賣完,請下次再來"); 64 return; 65 } 66 // } 67 68 } 69 } 70 }
2、測試同步鎖
1 package com.cn.donleo.thread.phone; 2 3 /** 4 * @author liangd 5 * date 2020-11-02 08:59 6 * code 線程同步測試 7 */ 8 public class TestMyPhone { 9 /** 10 * 手機屬於預定模式,分別由京東,淘寶,拼多多三個商家在賣 11 * 現在有華為P40PRO手機100部,怎么來實現 12 * 思考: 13 * 1.有一百部手機 14 * 2.京東,淘寶,拼多多是同時賣手機,屬於三個線程 15 * 3.有一個倉庫進行發貨,這個可以理解為main方法 16 * @param args 17 */ 18 public static void main(String[] args){ 19 MyPhone myPhone = new MyPhone(); 20 Thread tb = new Thread(myPhone,"淘寶"); 21 Thread jd = new Thread(myPhone,"京東"); 22 Thread pdd = new Thread(myPhone,"拼多多"); 23 tb.start(); 24 jd.start(); 25 pdd.start(); 26 27 } 28 }
3、測試死鎖
1 package com.cn.donleo.thread.phone; 2 3 /** 4 * @author liangd 5 * date 2020-11-02 09:42 6 * code 死鎖測試 7 */ 8 public class TestSynchronized { 9 /** 10 * 1、死鎖4個必要條件 11 * (1) 互斥條件:一個資源每次只能被一個進程使用。 12 * (2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。 13 * (3) 不剝奪條件:進程已獲得的資源,在未使用完之前,不能強行剝奪。 14 * (4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。 15 * 2、如何避免死鎖 16 * (1)加鎖順序(線程按照一定的順序加鎖) 17 * (2)加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己占有的鎖) 18 * (3)死鎖檢測 19 * 最簡單的方式:不要寫嵌套鎖就好 20 */ 21 public static void main(String[] args) { 22 /* 23 * 多次測試,出現死鎖,程序一直在運行,A和B都沒有吃到飯 24 */ 25 //匿名內部類,直接new接口必須實現接口中的抽象方法 26 new Thread(new Runnable() { 27 @Override 28 public void run() { 29 synchronized ("A") { 30 System.out.println(Thread.currentThread().getName() + "拿到A筷子了"); 31 //嵌套鎖 32 synchronized ("B") { 33 System.out.println(Thread.currentThread().getName() + "拿到A筷子了,同時拿到B筷子了,可以吃飯了"); 34 } 35 } 36 } 37 }, "男朋友").start(); 38 //再開一個線程 39 new Thread(new Runnable() { 40 @Override 41 public void run() { 42 synchronized ("B") { 43 System.out.println(Thread.currentThread().getName() + "拿到B筷子了"); 44 //嵌套鎖 45 synchronized ("A") { 46 System.out.println(Thread.currentThread().getName() + "拿到B筷子了,同時拿到A筷子了,可以吃飯了"); 47 } 48 } 49 } 50 }, "女朋友").start(); 51 } 52 }