一旦一个共享变量被volatile修饰,具备两层语义
1.保证了不间线程间的可见性
2.禁止对其进行重排序,也就是保证了有序性
3.并未保证原子性
可见性
package ;
public class volatileTest {
@SuppressWarnings("unused")
private static volatile int INIT_VALUE = 0;
@SuppressWarnings("unused")
private final static int MAX_LIMIT = 500;
public static void main (String[] args){
new Thread(()->{
int localvalue = INIT_VALUE;
while ( localvalue < MAX_LIMIT){
if (localvalue != INIT_VALUE){
System.out.printf ( "The value updated to [%d] \n",INIT_VALUE);
} localvalue = INIT_VALUE;
}
},"READER").start();
new Thread(()->{
int localvalue = INIT_VALUE;
while (INIT_VALUE < MAX_LIMIT){
System.out.printf ( "Update the value to [%d] \n",++localvalue);
INIT_VALUE = localvalue;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"UPDATER").start();
}
}
2.原子性
New Thread(()->{
while (INIT_VALUE < MAX_LIMIT) {
System.out.println ( "T1->" +(++INTT VALUE));
try {
Thread.sleep (10);
} catch(InterruptedException e) {
e.printstackTrace ();
}
}
},"ADDER-1" ).start ();
new Thread(o->{
while ( INIT_VALUE < MAX_LIMIT){
system.out.println ("T2->" +(++INIT_VALUE));
try {
Thread.sleep (10);
} catch (InterruptedException e){
e.printstackTrace ();
}
}
},"ADDER-1") .start ();
假设INTT_VALUE目前为10
1.read from main memory INIT_VALUE->10
2.INIT_VALUE=10+1
3.INIT_VALUE=11
线程1会覆盖掉11
4.read from main memory INIT_VALUE->10
5.INIT_VALUE=INIT_VALUE+1
6.INIT_VALUE=11
这个读出来会是12就无法解释了,拆的不对了
cpu轮转的时候没有轮转到就没有原子性了
Volatile做了什么?
每个线程操作数据的时候会把数据从主内存读取到自己的工作内存,如果他操作了数据并且写会了,他其他已经读取的线程的变量副本就会失效了,需要都数据进行操作又要再次去主内存中读取了。
volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。
Volatile是怎么保证不会被执行重排序的呢?
java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表:volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障。volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。如果现在我的变了flag变成了false,那么后面的那个操作,一定要知道我变了。我们要知道Volatile是没办法保证原子性的。
volatile关键字
1.保证重排序的是偶不会把后面的指令放到屏障的前面,也不会把前面的放到后面
2.强制对缓存的修改操作立刻写入主存
3.如果是写操作,他会导致其他cpu中的缓存失效
volatile修饰符适用于以下场景:某个属性被多个线程共享,其中有一个线程修改了此属性,其他线程可以立即得到修改后的值,比如booleanflag;或者作为触发器,实现轻量级同步。
volatile属性的读写操作都是无锁的,它不能替代synchronized,因为它没有提供原子性和互斥性。因为无锁,不需要花费时间在获取锁和释放锁_上,所以说它是低成本的。
volatile只能作用于属性,我们用volatile修饰属性,这样compilers就不会对这个属性做指令重排序。
volatile提供了可见性,任何一个线程对其的修改将立马对其他线程可见,volatile属性不会被线程缓存,始终从主 存中读取。
volatile提供了happens-before保证,对volatile变量v的写入happens-before所有其他线程后续对v的读操作。
volatile可以使得long和double的赋值是原子的。
volatile可以在单例双重检查中实现可见性和禁止指令重排序,从而保证安全性。
volatile与synchronized的区别
volatile只能修饰实例变量和类变量,而synchronized可以修饰方法,以及代码块。
volatile保证数据的可见性,但是不保证原子性(多线程进行写操作,不保证线程安全);而synchronized是一种排他(互斥)的机制。
volatile用于禁止指令重排序:可以解决单例双重检查对象初始化代码执行乱序问题。
volatile可以看做是轻量版的synchronized,volatile不保证原子性,但是如果是对一个共享变量进行多个线程的赋值,而没有其他的操作,那么就可以用volatile来代替synchronized,因为赋值本身是有原子性的,而volatile又保证了可见性,所以就可以保证线程安全了。