解决线程安全问题的三种方式(案例)

226 阅读2分钟

线程安全出现的原因

1、存在多线程并发

2、同时访问共享资源并且修改共享资源

如何解决: 让多个线程实现先后依次访问共享资源,这样就可以解决安全问题 线程同步的核心思想

加锁:把共享资源进行上锁,每次只能一个线程进入访问完毕后,然后其他线程才能进来

方案一(同步代码块)

作用:把出现线程安全问题的核心代码给上锁

原理:每次只能有一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行

public void dram(double money) {
    String sr = Thread.currentThread().getName();
    synchronized (this) {//对其上锁
        if(this.money >= money){
            System.out.println(sr + "取走了" + money);
           this.money -= money;
            System.out.println(sr + "取钱后余额为" + this.money);
        }else {
            System.out.println(sr + "来取钱 余额不足");
        }
    }
}

锁对象的要求

理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可,但是这样并不好,会影响其他无关线程的执行

锁对象的规范要求

1、建议使用共享资源作为锁对象

2、对于实例方法建议使用this作为锁对象

3、对于静态方法建议使用字节码(类名.class)对象作为锁对象

方案二(同步方法)

原理:每次只能有一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行

public synchronized void dram(double money) {
    String sr = Thread.currentThread().getName();
   
        if(this.money >= money){
            System.out.println(sr + "取走了" + money);
           this.money -= money;
            System.out.println(sr + "取钱后余额为" + this.money);
        }else {
            System.out.println(sr + "来取钱 余额不足");
        }
    }

同步方法底层原理

1、同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码

2、如果方法是实例方法:同步方法默认用this作为锁的对象,但是代码要高度面向对象

3、如果方法是静态方法:同步方法默认用类名.class锁的对象

方案三(LOCK锁)

public final ReentrantLock LOCK = new ReentrantLock(); //建议使用final修饰 因为锁对象的唯一性


public synchronized void dram(double money) {
    LOCK.lock();//对代码进行上锁
    String sr = Thread.currentThread().getName();

    try {  //try-finally捕获异常
        if(this.money >= money){
            System.out.println(sr + "取走了" + money);
           this.money -= money;
            System.out.println(sr + "取钱后余额为" + this.money);
        }else {
            System.out.println(sr + "来取钱 余额不足");
        }
    } finally {//放在try-finally里不管是否抛出异常都会对其进行解锁
        LOCK.unlock();
    }
}

微信图片_20220821183304.jpg