java内存模型与volatile关键词
作者:binjava的内存模型
线程工作内存中保存的都是该变量的副本,线程对变量对操作都是对工作内存中操作,而不是直接改主内存,最后更新到主内存中
volatile
我们知道(保证可见性、有序性)有2个能力,1个注意点:
1.保证变量可见性
2.禁止指令重排
3.不保证原子性
1.可见性
我知道java的变量都是存在堆内存中的,线程的本地方法栈存的是引用和基本类型的副本。
那么一个基本类型实际上是在「主内存」和「工作内存」中都有
线程修改时是先修改「工作内存」再同步至「主内存」。
线程读取时是先从「工作内存」读取
volatile关键词就是做了2件事情:
1.当写volatile变量时立即同步至「主内存」,
2.当读volatile变量时,将「工作内存」这个变量置为失效,即从「主内存」直接读取
2.禁止指令重排
这个可以参考单例模式中的饿汉模式
如果出现指令重排,可能会出现如下情况,即未初始化,就将引用指向完成,那么其他线程读取到Singleton!=null的,但是实际上还未初始化完成,就会出现线程安全问题:
memory = allocate(); //1.分配对象内存空间 instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成! instance(memory); //2.初始化对象
作为参考,正常顺序是:
memory = allocate(); //1.分配对象内存空间 instance(memory); //2.初始化对象 instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=null
3.不保证原子性
例如自增,实际上是先获取,再+1,并非原子性操作,会出现线程安全问题:
public static int count =0; public synchronized static void inc(){ count++; }
例如true、false判断,不需要保证原子性,但要保证可见性
volatile boolean isEnd; while (!isEnd){ System.out.println("isEnd"); }