并发1--原子性、可见性、有序性

什么是并发

个人理解,并发就是在短时间内多个进程或者多个线程去访问同一个资源,这个资源可以是接口,文件,数据库,或者共享变量等等。由于本人是java程序员,后文都是以java的角度来讲。

为什么要并发

为什么要并发,换言之,也就是为什么要使用多线程的问题。这个问题比较弱智,开发人员应该都清楚,多线程主要是提升任务执行的效率。

并发会产生什么问题

对于java而言,并发所产生的问题就是多线程“同时”去访问共享变量而导致的数据不一致。而局部变量是线程私有,也就不存在并发问题了。

怎么解决并发问题

解决并发问题就要考虑三个方面

  • 可见性
  • 原子性
  • 有序性

可见性

应用程序的全局变量、局部变量等都存在于机器的内存当中,内存当中的数据变化则是通过cpu的计算而改变的,因此cpu需要与内存交互,如读取数据、存储计算结果等,这个io操作是很难避免的。而由于计算机的存储设备与处理器的运算速度差距非常大,所以现代计算机系统都会增加一层读写速度尽可能接近处理器运算速度的高速缓存来作为内存和处理器之间的缓冲:将运算需要使用的数据复制到缓存中,让运算能快速进行,当运算结束后再从缓存同步到内存之中。

在多核机器中,不同cpu都有自己的高速缓存.

高速缓存从下到上越接近 CPU 速度越快,同时容量也越小。现在大部分的处理
器都有二级或者三级缓存,从下到上依次为 L3 cache, L2 cache, L1 cache. 缓
存又可以分为指令缓存和数据缓存,指令缓存用来缓存程序的代码,数据缓存
用来缓存程序的数据

L1 Cache,一级缓存,本地 core 的缓存,分成 32K 的数据缓存L1d 和32k 指
令缓存L1i,访问 L1 需要3cycles,耗时大约 1ns;

L2 Cache,二级缓存,本地 core 的缓存,被设计为 L1 缓存与共享的 L3 缓存
之间的缓冲,大小为 256K,访问 L2 需要 12cycles,耗时大约 3ns;

L3 Cache,三级缓存,在同插槽的所有 core 共享 L3 缓存,分为多个 2M 的
段,访问L3 需要 38cycles,耗时大约 12ns;

缓存不一致问题

cpu的高速缓存是不共享的。而线程是cpu调度的最小单元,当不同线程被不同cpu调度的时候,就可能产生数据不一致问题。
如:CPU-0 读取主存的数据,缓存到 CPU-0 的高速缓存中,CPU-1 也做了同样的事情,而CPU-1 把 count 的值修改成了 2,并且同步到CPU-1的高速缓存,但是这个修改以后的值并没有写入到主存中,CPU-0 访问该字节,由于缓存没有更新,所以仍然是之前的值,就会导致数据不一致的问题。

解决方案

针对于cpu缓存不一致问题,cpu厂商也给出了一些解决方案。

总线锁

当一个CPU 对其缓存中的数据进行操作的时候,往总线中发送一个Lock信号。其他处理器的请求将会被阻塞,那么该处理器可以独占共享内存。总线锁相当于把CPU和内存之间的通信锁住了,所以这种方式会导致 CPU 的性能下降,所以P6 系列以后的处理器,出现了另外一种方式,就是缓存锁。

缓存锁

如果缓存在处理器缓存行中的内存区域在LOCK操作期间被锁定,当它执行锁操作回写内存时,处理不在总线上声明 LOCK 信号,而是修改内部的缓存地址,然后通过缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域的数据,当其他处理器回写已经
被锁定的缓存行的数据时会导致该缓存行无效。 所以如果声明了CPU 的锁机制,会生成一个 LOCK 指令,会产生两个作用

  1. Lock 前缀指令会引起引起处理器缓存回写到内存,在 P6 以后的处理器中,
    LOCK 信号一般不锁总线,而是锁缓存
  2. 一个处理器的缓存回写到内存会导致其他处理器的缓存无效

处理器上有一套完整的协议,来保证缓存一致性,mesi协议,这个可以自行搜索。

有序性

有序性是指,编译器或者处理器并不会按照我们的代码顺序来执行,编译器或者处理器会按照执行性能更优的顺序来执行,这在单线程下是没有问题的,但是在多线程的场景下,可能会产生严重的问题。


        Thread t1 = new Thread(() -> {
            a = 1;
            x = b;
        });
        Thread t2 = new Thread(() -> {
            b = 1;
            y = a;
        });
        new Thread(()-> t1.start());
        new Thread(()->t2.start());
        t2.join();
        t1.join();
        System.out.println("x=" + x + "->y=" + y);

这段骀荡可以猜猜可能有几个结果。

原子性

原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值