目录
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题
1.什么是线程安全问题 ?
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题
场景: 小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元, 如果小明和小红同时来取钱,并且2人各自都在取钱10万元,可能会出现什么问题呢?
解决方法一:同步代码块
作用: 把访问共享资源的核心代码给上锁,以此保证线程安全
synchronized(同心锁){
访问共享资源的核心代码
}
原理: 每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
同步锁的注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug锁对象的使用规范:
建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象
对于静态方法建议使用字节码(类名.class)对象作为锁对象
public void drawMoney(double money) {
// 同步代码块
// 先搞清楚是谁来取钱
String name=Thread.currentThread().getName();
synchronized (this){
// 判断余额是否足够?
if (this.getMoney()>=money){
System.out.println(name+"取钱成功"+money);
// 更新余额
this.money-=money;
System.out.println(name+"来取钱成功后剩余"+this.money);
}
else {
System.out.println("余额不够哦");
}
}
}
解决方法二:同步方法
作用:把访问共享资源的核心方法给上锁,以此保证线程安全
修饰符 synchronized 返回值类型 方法名称 (){
操作共享资源的代码
}
原理:每次只能进入一个线程进去,进去后会上锁,出来后会自动解锁,同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码
如果方法是实例方法:同步方法默认用this作为的锁对象
如果方法是静态方法:同步方法默认用类名.class作为的锁对象是同步代码块好还是同步方法好一点?
范围上 : 同步代码块锁的范围更小,同步方法锁的范围更大
可读性: 同步方法更好
// 同步方法
public synchronized void drawMoney(double money) {
// 先搞清楚是谁来取钱
String name=Thread.currentThread().getName();
// 判断余额是否足够?
if (this.getMoney()>=money){
System.out.println(name+"取钱成功"+money);
// 更新余额
this.money-=money;
System.out.println(name+"来取钱成功后剩余"+this.money);
}
else {
System.out.println("余额不够哦");
}
}
解决方法三:创建锁对象
作用:可以在指定位置处加锁,解锁,更灵活
final private Lock lk =new ReentrantLock();
lk.lock(); // 加锁
lk.unlock(); // 解锁 一般会放在try...catch finally里
public void drawMoney(double money) {
// 先搞清楚是谁来取钱
String name=Thread.currentThread().getName();
lk.lock(); // 加锁
// 判断余额是否足够?
try {
if (this.getMoney()>=money){
System.out.println(name+"取钱成功"+money);
// 更新余额
this.money-=money;
System.out.println(name+"来取钱成功后剩余"+this.money);
}
else {
System.out.println("余额不够哦");
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
lk.unlock(); // 解锁
}
}