锁对象
临界区:临界区是一个特殊的代码段,该代码段访问某种特殊的公共资源,该资源同一时间只允许一个线程使用。
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 }