多线程带来的风险——线程安全

一、什么是线程安全
如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的。

二、线程不安全的原因
1、线程是抢占式执行
线程之间的调度完全是由内核负责,用户代码感知不到,也无法控制。
2、自增操作不是原子的
每次++都能拆成三个步骤
(1)把内存中的数据加载到 cpu 上
(2)cpu 进行++操作
(3)将++后的值重新写入内存中
当 CPU 在执行三个步骤中的任何一个步骤的时候都有可能被调度器调走,让其他线程来执行。

注:什么是原子性
我们把一段代码想象成一个房间,每个线程就是要进入这个房间的人。如果没有任何机制保证,A进入房间之后,还 没有出来;B 也可以进入房间,打断 A 在房间里的隐私。这个就是不具备原子性的。
那我们应该如何解决这个问题呢?只要给房间加一把锁,A 进去就把门锁上,其他人是不是就进不来了。这样就保证了这段代码的原子性了。
有时也把这个现象叫做同步互斥,表示操作是互相排斥的。
不保证原子性会给多线程带来什么问题:如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是错误的。

3、多个线程尝试修同一个变量
这也是由于多个线程修改同一个变量不是原子的。
4、可见性
为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线 程之间不能及时看到改变,这个就是可见性问题。这样就会造成线程不安全。
5、指令重排序
什么是指令重排序?
例如家人让你去买菜,让你买黄瓜,西红柿和肉;正常的顺序是先去买黄瓜,然后西红柿,最后买肉,但是由于超市入口处就是肉,之后是西红柿,出口处是黄瓜,代码就会重排序,会先买肉,然后西红柿,最后买肉,最后的结果都是买这三样食材。单线程这样的优化没有问题,但是多线程就会出现线程不安全。

三、如何解决线程不安全
1、加锁(synchronized 关键字-监视器锁monitor lock) 实现原子性
synchronized的底层是使用操作系统的mutex lock实现的。
锁的特点:互斥性,同一时刻只能有一个线程获得锁。其他线程如果也尝试获取锁,就会发生阻塞等待。一直等到刚才的线程释放锁,此时剩下的线程再重新竞争锁。
当线程释放锁时,JMM会把该线程对应的工作内存中的共享变量刷新到主内存中。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内 存中读取共享变量。
1.1、synchronized 几种常见的方法:
1)加到普通方法之前:表示锁 this;

public class SynchronizedDemo {
   
    public synchronized void method() {
   
    }
    public static void main(String[] args) {
   
        SynchronizedDemo demo = new SynchronizedDemo();
        demo.method(); // 进入方法会锁 demo 指向对象中的锁;出方法会释放 demo 指向的对象中的锁
    }
}

2)加到静态方法之前&

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值