并发编程三大特性
1.原子性
2.有序性
3.可见性
可见性
由于缓存机制,导致不同cpu核心操作同一个变量的时候会出现数据不一致情况
三级缓存
现代一颗cpu一般会包含多个cpu核 如4颗8核
每个cpu核包含L1,L2缓存,每颗cpu包含L3缓存.L1,L2是核独享,L3是cpu的所有核共享
cpu读取变量数据流程
1.先从L1缓存找
2.再从L2缓存找
3.然后从L3缓存找
4.最后从内存中找
缓存行(块)
缓存变量并不是按照单个变量的值一个一个缓存的 多个变量的值组成一个块一起读到缓存中
而是按照程序的局部性原理 以一块一块为读取单位从内存中读取到L3,L2,L1缓存中去
cpu的一个缓存行是64个字节
CPU缓存一致性协议
两颗cpu之间的缓存行数据需要保持一致的机制
当cpu不同核之间缓存了同一块缓存行的时候,某个核上的线程对缓存行的数据进行修改后cpu会有内部机制通知其它核需要去内存中获取最新数据刷新缓存行来保证数据的一致性
由于有了这种机制 当多线程修改的数据位于同一缓存行的时候反而会相互干扰,降低执行效率
硬件层面MESI Cache一致性协议
线程本地缓存
因为一个线程执行的时候必须对应唯一的CPU的某个核,所以线程本地缓存就是某个CPU核对应的L1,L2高速缓存
线程的可见性
线程操作一个变量的时候默认是先把变量从内存中拷贝到线程自己的缓存中,而不是一直操作内存
多个线程操作同一个变量的时候就会涉及到每个线程缓存中的值是否一致的问题
一个线程改了公共变量的值,另外一个线程是否可以获取到公共变量最新的值。这就是多线程的可见性
默认情况下一个线程修改变量值.另外一个线程是获取不到最新值的
volatitle 保持可见性
对volatitle修饰的内存地址 任何线程读取的时候都会从主内存去读取,而不是从默认的线程自身缓存中读取
这样所有线程读取的变量的值保证都是最新的
有序性
cpu乱序执行指令
有序性
两个代码语句之间,有可能是就交换顺序执行的
并不是按照开发人员写的语句顺序从上往下依次执行
每一条开发写的代码语句被编译后都会被分割成好几条汇编指令
乱序执行
提高CPU的执行效率
前后两条指令没有依赖关系的时候,这两条指令的执行顺序有可能被调整
不影响单线程的最终一致性的情况下,执行指令的顺序有可能是乱序的
内存屏障
用内存屏障机制来阻止指令的乱序执行
内存屏障就是一条特殊的指令
不同的CPU中设置的内存屏障指令都是不一样的
原子性
多线程访问共享资源的时候会产生竞争
线程同步
解决原子性执行
锁机制
上锁的本质
把原来的并发操作变成了序列化操作
锁就是一个任意类型的变量,哪个线程先通过cpu获取了这个变量的控制权,这个线程的代码指令就先执行
synchronized
每个线程操作完成后一定会把数据刷新到主内存 保证可见性
每个线程的代码是串行执行 保证原子性
存储器分级