引发线程安全的原因和解决方法

1.多个线程在系统中抢占式执行

操作系统中有很多的线程,这些线程的调度都是有系统来完成的,但是呢系统的调度是随机的,我们并不知道系统的调度顺序,这样就可能导致多个线程在cpu的调度中出现问题.

2.多个线程对同一个变量执行修改操作

当多个线程同时对一个变量进行修改时,有可能数据更新不够及时导致其他线程进行重复操作.

3.修改操作并不是原子的

原子性,我们在学习数据库的时候接触过,整个执行过程是完整的,在执行完成之前是不会停止的,我们使用的大部分操作转变为cpu指令时就会变成好几步.这也就导致了在多线程的执行的过程中会发生线程安全问题.

class  Counter{
    public int count = 0;
    public void increase(){
        count++;
    }
    public void print(){
        System.out.println(count);
    }
}
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread a = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        Thread b = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        a.start();
        b.start();
        a.join();
        b.join();
        counter.print();
    }
}

这一段代码理论上执行的结果是100000,但真正的结果却并不是,而且不管你运行几次,你每次运行出来的结果都是不一样的发生这一现象的原因就是上面的二三两条.

我们根据原因来解决问题,我们只要让这个counter++操作变成一个原子操作就可以了.这个时候可以通过synchronized关键字来完成.这个关键字可以在一个线程修改变量的时候进行上锁,这个时候如果有其他线程想要修改这个变量就必须要等待,等上一个线程结束了解锁了才可以进行操作.这个就保证了同一时间只有一个线程对这个变量进行了修改 .

 

4. 内存可见性

由于编译器是非常智能的它可以自动帮我们优化代码,让这个代码的运行速度更快,但是呢并不是每次优化都是好的,但编译器监测到一个变量长时间不发生改变并且需要经常调用,就会将这个变量从内存转移到寄存器中.这样可以大大加快编译速度.但是一旦我们改变了这个变量就会发生线程安全问题.

5.指令重排序

这个问题同样也是由于编译器自动优化导致的,不过这个问题有点抽象,我用一幅图来解释

 

 这样通过系统的优化只要两步就可以完成任务.但是这样的优化有时候可能出现问题,比如老师U盘可能还没有用完就被你拿走了.

针对四五两个问题可以通过volatile这个关键字来解决,这个关键是用来修饰的变量,系统就会知道这个变量可能会发生改变就不会自动优化了.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值