并发编程-java内存模型
文/属衣

Java内存模型的抽象结构示意图
Java内存模型包含主内存和工作内存(本地内存),所有的变量都存储在主内存中。线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。
A线程与B线程的通信过程:1)线程A把本地内存共享变量刷新到主内存 2)B线程读取A线程已更新过的共享变量
但实际情况,A线程与B线程的通信会出现“脏读”等问题。
1.原子性
原子性即一个操作或者多个操作要么全部成功执行,要么都不执行。
int i=10;① i--;② j=i; ③
只有语句①是原子性,其余需要先读取变量再进行赋值,所以是非原子性。
2.可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
int i=10;① j=i;②
线程A在执行语句①,并未将赋值后的结果刷新到主内存,线程B进行语句②操作时,导致读取到旧值,无法保证可见性。
3.有序性
有序性即程序执行的顺序按照代码的先后顺序执行。一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。
线程A:context = loadContext();①inited = true; ②线程B:while(!inited ){ sleep()}initConfig(context);①②可能会被重排序,假如线程A先执行了语句②,会导致线程B直接跳过while循环,导致initConfig(context)失败!volatile
volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。
1.使用volatile修饰的共享变量(类的成员变量、类的静态成员变量),保证了可见性与有序性(禁止进行指令重排序)。
线程A:boolean stop = false;while(!stop){ doSomething();}线程B:stop = true;public class Test { public volatile int inc = 0; public void increase() { inc++; } public static void main(String[] args) { final Test test = new Test(); for(int i=0;i<10;i++){ new Thread(){ public void run() { for(int j=0;j<1000;j++) test.increase(); }; }.start(); } while(Thread.activeCount()>1) //保证前面的线程都执行完 Thread.yield(); System.out.println(test.inc); }}increase不是原子性的,假如线程1执行时inc=10,线程1先将inc读取到工作内存(此时保证读取到的是最新结果),此时被阻塞。线程2将inc读取到工作内存并进行+1操作,然后放入主内存inc=11,线程1接着进行inc+1操作(inc已经被读取过了inc=10),所以会将inc=11放入主内存。volatile的实现原理
1.可见性
处理器为了提高处理速度,不直接和内存进行通讯,而是将系统内存的数据独到内部缓存后再进行操作,但操作完后不知什么时候会写到内存。
如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。 这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。
但这时候其他处理器的缓存还是旧的,所以在多处理器环境下,为了保证各个处理器缓存一致,每个处理会通过嗅探在总线上传播的数据来检查 自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改了,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作时,会强制重新从系统内存把数据读到处理器缓存里。 这一步确保了其他线程获得的声明了volatile变量都是从主内存中获取最新的。
2.有序性
Lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。
volatile的应用场景
1.状态标记
2.单例double check
class Singleton{ private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { synchronized (Singleton.class) { if(instance==null) instance = new Singleton(); } } return instance; }}

浙公网安备 33010602011771号