Java共享变量的内存可见性问题:
Java内存模型中,Java中所有的变量存在于主内存,当线程需要使用变量时,会复制一份变量到线程的工作内存,处理完成后更新到主内存
但线程之间一级缓存互不可见,导致线程B修改内存变量后,线程A还是从自己的一级缓存中获取到了上次修改并缓存的变量值
- 线程对变量的操作必须在工作内存中进行,线程的工作内存相互独立,volatile强制将工作内存修改的值立即写入主内存
作用:保证了对变量的更新对其他线程马上可见
当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。
当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。
指令重排:Java内存模型允许编译器和处理器对指令重排序以提高运行性能,只针对不存在数据依赖性的指令重排序。
什么是数据依赖性?
如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。
举例:instance= new Singleton();
由于new对象的操作不是一个原子性操作
1.分配内存空间
2.执行构造方法,初始化对象
3. 把这个对象指向这个空间
正常指令为123, 但A线程执行了132 到达3此时instance还没有完成构造,B线程认为instance==null
在单线程下,指令重排不会导致最终结果不同,但多线程下就可能有所不同
作用:禁止指令重排
编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
- 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
- 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
使用volatile进行修饰:
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
- 禁止指令重排
通常来说,使用volatile必须具备以下2个条件:
- 对变量的写操作不依赖于当前值(反例如自增自减)
- 该变量没有包含在具有其他变量的不变式中(反例如两个变量比较的执行逻辑)