鎖對象
臨界區:臨界區是一個特殊的代碼段,該代碼段訪問某種特殊的公共資源,該資源同一時間只允許一個線程使用。
Java中可以使用鎖對象創造一個臨界區:
1 myLock.lock(); 2 try { 3 關鍵代碼 4 } finally { 5 myLock.unlock(); 6 }
使用這種結構可以確保關鍵代碼不會同時被多個線程執行,線程想要執行關鍵代碼必須先獲取“鎖”,“鎖”只能被一個線程持有,在該線程將“鎖”釋放前,其他線程因為獲取不到鎖而被阻塞,“鎖”被釋放的時候,之前因為獲取這個鎖被掛起的線程都會被喚醒,共同競爭着去獲取鎖。
將釋放鎖的操作寫在finally里是很重要的,防止某個線程執行關鍵代碼的時候因為拋出異常而沒有將鎖釋放。

1 package learnspringboot.xiao.learnjava.thread; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.Lock; 7 import java.util.concurrent.locks.ReentrantLock; 8 9 /** 10 * @author xzy 11 * @date 2019-12-13 11:01 12 * 說明:模擬銀行轉賬 13 */ 14 public class Bank { 15 private Map<String, Double> accountMap = new HashMap<>(); 16 private Lock myLock = new ReentrantLock(); 17 private Condition haveMoney = myLock.newCondition(); 18 19 public Bank() { 20 this.accountMap.put("ZhangSan", 100.0); 21 this.accountMap.put("WangWu", 1000.0); 22 } 23 24 public void transfer(String from, String to, Double money) { 25 //線程執行后面的代碼段需要先獲得鎖,獲取不到就掛起。 26 myLock.lock(); 27 System.out.println("當前獲得鎖的線程:" + Thread.currentThread().getName()); 28 System.out.println(from + "想給" + to + "轉" + money + "元," + from + "現在有" + accountMap.get(from) + "元"); 29 try { 30 while (accountMap.get(from) < money) { 31 System.out.println("線程阻塞:" + Thread.currentThread().getName()); 32 //獲取到鎖的線程需要等待本條件成立,條件成立前將鎖釋放,線程阻塞。 33 haveMoney.await(); 34 } 35 accountMap.put(from, accountMap.get(from) - money); 36 accountMap.put(to, accountMap.get(to) + money); 37 System.out.println(from + "轉給" + to + money + "元 線程:" + Thread.currentThread().getName()); 38 //通知因本條件而掛起的線程,條件現在可能已經滿足,可以試着再去獲取肯看。 39 haveMoney.signalAll(); 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } finally { 43 myLock.unlock(); 44 } 45 } 46 47 public static void main(String[] args) { 48 Bank bank = new Bank(); 49 Thread thread1 = new Thread(() -> { 50 bank.transfer("ZhangSan", "WangWu", 500.0); 51 }); 52 Thread thread2 = new Thread(() -> { 53 bank.transfer("WangWu", "ZhangSan", 1000.0); 54 }); 55 Thread thread3 = new Thread(() -> { 56 bank.transfer("ZhangSan", "WangWu", 500.0); 57 }); 58 thread1.setName("張三給王五500 x1"); 59 thread2.setName("王五給張三1000"); 60 thread3.setName("張三給王五500 x2"); 61 thread1.start(); 62 thread2.start(); 63 thread3.start(); 64 while (true) { 65 66 } 67 } 68 }