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(); // 解锁 } }