连个线程同时操作同一个公共资源,容易造成资源错误
加入两个人代表两个线程他俩同时去取钱10元,但是账户只有10元,如果不处理线程安全容易造成两人都能取出来钱;
解决线程安全-----加锁
让多个线程先后依次的访问共享资源
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
实现同步代码块
- 选中共享资源的代码CTRL+T 然后选中 synchronized
//锁对象:必须是一个唯一的对象(同一个地址)锁对象可以是任何对象甚至是字符串 synchronized(锁对象){ //...访问共享数据的代码... }
- 对于静态方法建议使用字节码(类名.class)对象作为锁对象。
public static void test(){ synchronized(类名.class){ } }- 建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象。
- this 代表着是共享账户
public void test(){ synchronized(this){ } }
将处理共享资源的方法上加上 syschronized 关键字 表示当前是一个同步方法
public synchronized void drawMoney(double money) { // 先搞清楚是谁来取钱? String name = Thread.currentThread().getName(); // 1、判断余额是否足够 if(this.money >= money){ System.out.println(name + "来取钱" + money + "成功!"); this.money -= money; System.out.println(name + "来取钱后,余额剩余:" + this.money); }else { System.out.println(name + "来取钱:余额不足~"); } }
- 如果是实例方法默认使用this作为锁对象
- 如果是静态方法默认使用类名.class作为锁对象
- 底层原理:
- 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码
范围上:同步代码块锁的范围更小,同步方法锁的范围更大
可读性:同步方法更好
怎么实现
- 创建锁对象,放在方法外面,表示这个是当前对象的锁 加上final修饰表示当前锁无法被修改
// 创建了一个锁对象 private final Lock lk = new ReentrantLock();- 在方法内部需要加锁的地方加锁
- 在需要的地方进行解锁
- 以上两步一般使用try-catch-finally修饰,以防中间代码出现问题也能进行解锁
实现Lock锁
// 创建了一个锁对象 private final Lock lk = new ReentrantLock(); public void drawMoney(double money) { // 先搞清楚是谁来取钱? String name = Thread.currentThread().getName(); try { lk.lock(); // 加锁 // 1、判断余额是否足够 if(this.money >= money){ System.out.println(name + "来取钱" + money + "成功!"); this.money -= money; System.out.println(name + "来取钱后,余额剩余:" + this.money); }else { System.out.println(name + "来取钱:余额不足~"); } } catch (Exception e) { e.printStackTrace(); } finally { lk.unlock(); // 解锁 } } }
同一个锁对象可以跨方法锁住多个方法