Java 并发问题、产生的原因及解决方法

  1. 并发问题是什么

并发问题就是线程不安全,当多线程同时读写一个变量是,因为原子性,缓存可见性,指令重排序等原因,导致变量的实际执行结果和预期不一致

  1. 并发问题出现的场景

静态变量,多线程访问类的同一实例

静态变量,多线程访问类的不同实例

实例成员变量,多线程访问同一实例

  1. 并发问题产生的原因

(1)线程切换导致原子性问题

进程和线程本质上是增加并行的任务数量来提升CPU的利用率。原子性是指一个操作要么全部执行完毕,要么全部不执行。并发读写同一变量的线程之间任务切换时,如果对变量的读写操作不是原子性的,就会导致并发问题。

(2)多核cpu缓存导致的不一致问题

由于CPU执行速度远远快于内存存取速度,所以为了提高CPU利用率,系统在CPU和内存之间添加了高速缓存cache,cache通常减少IO速度来提高CPU利用率。CPU读取数据时,先访问cache,如果cache命中,就不再访问主存,直接返回cache中的数据。当CPU写数据到内存时,会先写到cache,通常不会马上写回主存,只有cache要被替换或者cache无效时,才会写回主存。

单核CPU不会有不一致问题,这个问题只会出现在多核CPU上,现代处理器大部分都是多核心CPU。

在多核CPU上,每个内核都有自己的独立缓存。当多线程读写同一变量时,如果线程运行在不同内核上,那么它们对同一变量的读写操作就分别在不同的cache中执行。每个cache都是独立的,互相不可见,单个cache中对变量缓存的操作不会影响别的cache,也不知道别的cache中的数据,这样就会导致最终结果的不可控。

(3)指令重排序问题

为提升内核自身执行速度,内核内部使用流水线并行执行多条指令。

指令之间通常因数据相关,名称相关,控制相关造成的依赖关系,这导致流水线中后面的指令需要等待前面的指令完成后才能执行,大大降低了流水线的并行度

为提高流水线并行性能,编译器和内核通常会对指令进行静态和动态调度,在不影响单线程执行结果的前提下,把无关的指令插入到指令空闲的位置提前执行,以提升流水线性能。

解决方法

同步方案:如果线程间需要协作,即一个线程的执行需要依赖其他线程执行的结果,那么就说线程之间是需要同步的,一般添加互斥锁解决

  1. synchronized

  1. Lock

  1. volatile

无同步方案:但是互斥锁会阻塞其他线程的执行,效率较低,所以如果线程间不需要协作,即一个线程的执行不依赖于其他线程的执行结果,这种情况就不需要使用互斥锁的方案。

  1. TheadLocal:当多个线程共同操作一个对象,但是互不影响,不需要同步时,可以不加互斥锁,使用ThreadLocal保证每个线程都有共享对象的一个备份,线程间数据互相隔离,就互不影响。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值