1、保证变量在多线程之间可见性
2、禁止指令重排(不保证原子性,线程不安全)
可见性:可见性是指多线程情况下能够自动发现volidate修饰的变量的最新值。
(如果对Java内存模型比较了解的话会知道,每个线程都会被分配一个线程栈,如果对象是多线程间的共享资源时,当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的值load到线程栈中,建立一个变量副本,之后线程操作的都是副本变量,当修改完副本变量之后,会写回值到主内存。但由于线程栈是线程间相互隔离的,即多线程间不可见,如果有其他线程修改了这个变量,但还未写回主内存或者更新主内存后,其他线程读取的仍是自己线程栈的副本时,就会出现问题。而volidate则是用来保证可见性。即一个线程对共享变量的修改,能够及时被其他线程看到。)
指令重排:在执行程序时为了提高性能,编译器和处理器通常会对指令做重排序。虽然代码有顺序,但是执行的时候不一定按照代码顺序执行。这样多线程情况下可能就会出问题(但是很少,因为Java、cpu、内存之间会有一套严格的指令重排顺序)。
第一张图没有加volidate,第二张图加了volatile,对比汇编指令,加上volidate之后,会加lock锁。通过lock锁增加内存屏障。
不保证原子性:原子性就是这个操作不可再拆分。volidate并不能保证原子性,如i++,分为load、increment、store、Memory Barriers即装载、新增、存储、内存屏障,第四步保证jvm让最新的变量在所有线程可见,从load、increment到store是线程不安全的,中间如果其他的cpu线程修改值将会出现问题。
可见性实现原理是通过store和load指令完成的,在对volatile变量执行写操作的时候,会在操作后加一条store指令,即强迫该线程将最新值写入内存;在读操作的时候,会加入load指令,强迫从主内存中获取变量最新值。(线程之间变量值通过主内存来完成)