01-并发编程bug源头:可见性、原子性和有序性

一、可见性

  是指多线程在不同cpu缓存(多核)中对同一个变量进行修改,导致的不可见

 

 

二、原子性

  指线程间切换导致的原子性问题

  一条编程语句执行往往包含多条CPU指令,操作系统做任务切换,可以发生在任意一条cpu指令执行完

  比如count+=1;这条语句包含3条cpu指令:

  1.把变量count从内存中加载到cpu寄存器;

  2.在cpu寄存器中执行+1操作

  3.把结果写入内存(也可能是因为缓存机制写入的是cpu缓存而不是内存)

 

 

三、有序性

 因为编译器优化导致程序语句的执行顺序发生变化。

经典案例就是单例模式中利用双重检查创建单例对象。

 1 public class Singleton{

3 private static Singleton instance; 4 5 private static Singleton getInstance(){ 6 7   if(instance==null){ 8 9     sychronized(Singleton.class){ 10 11       if(instance==null){ 12 13         instance=new Singleton(); 14 15       } 16 17     } 18 19   } 20 21   return instance; 22 23 } 24 25 }

问题出在new操作上,我们以为的操作是这样的:

  1.分配一块内存M;

  2.在内存M上初始化Singleton对象

  3.将地址M赋值给变量instance

而实际上经过编译优化的执行操作是这样的:

  1.分配一块内存M;

  2.将地址M赋值给变量instance

  3.在内存M上初始化Singleton对象

 

 

解决办法:把属性Singleton instance加修饰符volatile就完美了,对instance进行volatile语义声明,就可以禁止指令重排序。

四、关于volatile资料参考:https://www.jianshu.com/p/ccfe24b63d87
volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略。
volatile 的特性
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。

 

五、关于cpu缓存、线程工作内存:https://blog.csdn.net/u013851082/article/details/70314778/

posted @ 2019-10-11 00:11  45°仰望星空  阅读(172)  评论(0编辑  收藏  举报