
自己定义一个锁
上锁,解锁
package com.itheima.d3_thread_safe;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Account {private String cardId;private double money; // 账户余额// 自己定义一个锁// 在这里创建一个锁对象(外面new了一个Account(账户类)就会自动创建锁对象)// final修饰后:锁对象是唯一和不可替换的,非常专业 (每次new一个账户,就会生成一个新的锁对象),自己拿自己的锁private final Lock lock = new ReentrantLock(); // 加final的好处就是变成一个常量(常量对象)只能赋值一次public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}public String getCardId() {return cardId;}public void setCardId(String cardId) {this.cardId = cardId;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}/*** 在账户类里面定义一个取钱方法drawMoney* @param money 代表用户取的钱** // 方式二: 同步方法 : 把出现线程安全问题的核心方法给上锁,这样每次只能有一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行*/public void drawMoney(int money) {// 0. 先获取是谁来取钱,线程的名字就是人嘛// 使用Thread.currentThread() 获取当前线程对象(谁调用该线程,就获取哪个线程对象),然后使用getNmae方法获取该线程名字String name = Thread.currentThread().getName();// 同步代码块: 给会出现线程安全问题的代码块给上锁// synchronized ("heima") 一样的,都是唯一对象,这个字符串对象都在常量池中,表唯一// 将字符串对象,改成 this == acc(表示当前对象,当前调用drawMoney方法的对象,这样就不会让账户冲突// this == acc 共享账户 不会让账户冲突 -- 如果是小明小红账户取钱(调用drawMoney方法),那this代表小明小红(这样就实现了共享)// 直接双引号:因为会在字符串常量池中产生(唯一的一份),满足了锁的唯一性(锁用任意唯一的对象不好)// 在// 方式三:自定义锁,在会出现线程安全问题的代码块给上锁lock.lock(); // 上锁try {// 1. 判断账户是否够钱if (this.money >= money){// 2. 取钱System.out.println(name + "来取钱成功,吐出:" + money);// 3. 更新余额this.money -= money;System.out.println(name + "取钱后余额剩余:" + this.money);// System.out.println(10/0); // 如果这里出异常了,那么后面的锁则不会解开,所以锁最好定义在finally代码块中}else {// 余额不足System.out.println(name + "来取钱,余额不足");}} catch (Exception e) {e.printStackTrace();}finally { // finally 如果异常与否,都会执行这行代码lock.unlock(); // 解锁}}}
