Violate关键字
引用:https://www.cnblogs.com/dolphin0520/p/3920373.html
这篇博客讲的比较好。
个人理解:violate能够保证可见性和有序性,
可见性:
例子如下:
//线程1
boolean stop = false;
while(!stop){
doSomething();
}
//线程2
stop = true;
当线程1,stop为共享变量,放在主存中,线程1读取一份数据放到工作内存中,进行while循环,而线程二,运行时也读取了stop变量到工作内存中,但是单线程二赋值stop为true时,没发立即将stop的值写入到主存中,只会写入到工作内存。
当将stop用violate关键字修饰时,发生修改后,会产生3中效果。
1.将stop值,立即写入主存。
2.将线程1中工作内存的stop值缓存行,置为无效。
3.由于线程1读取stop值时,工作内存无效,会重新去主存读取。
顺序性:
例子如下:
//线程1:
context = loadContext(); //语句1
inited = true; //语句2
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
inited被violate修饰时,保证context已经初始化完毕,效果是:
1.当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。
2. 在进行指令优化时,不能将在对volatile变量访问前的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
violate没发保证原子性
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);
}
}
violate不能保证原子性,violate的使用场景是,
- 状态标记量
volatile boolean flag = false;
while(!flag){
doSomething();
}
public void setFlag() {
flag = true;
}
volatile boolean inited = false;
//线程1:
context = loadContext();
inited = true;
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
- 双重循环校验
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;
}
}
双重校验理解:
1.第一次判断(instance==null)是为了减少线程访问时都需要加锁后在判断,影响效率。所以在加锁前做了判断,如果获取锁后,继续判断是否初始化成功,如果没成功在初始化。
2.此时初始化,是分成3个部分,
a.开辟内存对象。
b.初始化对象。
c.将内存对象引用赋值给变量。
b和c可能会发生重排序,这时如果有线程走到第一次判断,则会拿到一个未初始化的对象,运行会发生错误。