自己定义一个锁
上锁,解锁
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(); // 解锁
}
}
}