并发编程会导致什么问题

并发编程导致的问题

导致有序性的原因是编译优化

这个怎么说
比如下面的例子 双重检查锁的单例模式

public class Singleton{
    private static Singleton  singleton;
    private Singleton(){
        
    }
    public static Singleton getSingle(){
        if(singleton==null){

            synchronized(Singleton.class){
                if(singleton==null){
                    // Single 1
                    this.singleton=new Singleton();
                }
            }
        }

    }

}


上面的代码如果多个线程并发执行 是存在问题的
这是因为this.single=new Single(); 翻译成汇编指令 将执行三部

  1. 申请开闭内存空间M
  2. 在内存M上初始化 Single()对象
  3. 将新开辟的地址赋值给Single
    但是编译优化使代码发生了重排序
    原本 1 2 3的执行顺序 变成了 1 3 2的执行顺序

那如果有两个线程A和B并发执行 getSingle()方法
线程A this.single=new Single(); 时cpu执行权被线程B抢去
该语句不是原子的 会被翻译为 汇编指令分三步执行 执行顺序 为 1 3 2 在执行完第3处时被线程B抢去

线程B执行第一个if语句发现 singleton不为空
直接返回 注意此时返回的是尚未初始化的Java对象

导致可见性的原因是缓存

CPU内部有块高速缓存的区域 如果有个变量A 处理器会优先通过解码内存地址直接访问高速缓存
如果在缓存中找到就成为命中 则直接读取数据
如果未命中 在从主内存中加载 并存入相应的缓存行 但是这个过程 会导致处理器停顿而不能执行其他指令

简而言之 高速缓存会对程序中访问的每个变量保留一份相应的内存空间
另外
cpu和内存之间存在较大的速度差异
为了解决这个问题 在每个cpu中引入了缓存的概念
但是不同CPU之间的缓存时不可见的
这就导致如果多个线程 操作同一个变量 可能某个线程的修改就会被擦除

切换线程会带来原子性问题

由于IO太慢 IO怎么慢了 如果一个进程进行一个IO操作 例如读文件 这个时候该进程可以把自己标记为"休眠"并让出CPU的使用权 待文件读取内存 操作系统会把这个休眠的进程唤醒 唤醒后的进程就有机会重新获得CPU的使用权 在等待IO时会释放CPU使用权 是为了让CPU在这段等待时间里可以做别的事情 这样CPU的使用率就提高了
如果这个时候另外一个进程也读文件 读文件的操作就会排队磁盘驱动在完成一个进程的读操作后,发现有排队的任务,就会立即启动下一个读操作

在高级语言中一条语句往往需要多条CPU指令完成 但是任务切换发生的最小单元确是某个Cpu指令

count+=1;翻译为汇编则至少要三条指令

  1. 需要把变量 count 从内存加载到 CPU 的寄存器
  2. 在寄存器中执行 +1 操作
  3. 将结果写入内存(缓存机制导致可能写入的是 CPU 缓存而不是内存)

任务切换发生的最小单元是某个CPU指令

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值