首先解释一下,什么叫指令排序:
案例一:
重排序是这样的概念,
比如
int i=1;
boolean flag=true;
i=2;
flag=false;
你代码是这么写的,但是真正在运行的时候,jvm可能先执行flag=false,然后执行i=2.
这就是指令重排序,但是他会保证你这段代码执行完确保你i=2,flag=false,只不过中间过程,
他可能会先给flag赋值为false,然后再给i赋值2
这在单线程下是没有问题的,但是多线程就有问题了
意思就是排序的前提是执行结果要准确,上面案例两个变量的操作指令顺序发生了变化,但是,最终结果是对的
如果你想并发的情况下,消除因为指令重排导致的一些问题,此时代码就可以改为
volatile int i=1;
boolean flag=true;
i=2;
flag=false;
此时,前三行代码编译后的字节码范围内的指令,是不能够进行重排序的,所以,flag=false指令一定是最后执行
案例二:
-------thread1-------------
obj=createObj()
init=true
上面2行是线程1执行的,下面是线程2执行的
------------thread2-------------
while(!init){
sleep();
}
useobj()
以上代码的意思是 线程1创建完obj后,赋值init为true,表示obj创建成功,然后线程2在循环判断init是不是true,如果不是true,则一直睡,如果是true,则使用obj。假如
obj=createObj() 执行了10秒,后执行init=true。但是此时若发生指令重排序,init=true 先执行了,然后再执行obj=createObj(),那此时线程2的逻辑就错了,判断的时候,init是true,但是obj此时还没有创建出来
所以第二个案例的优化方案就是给obj变量加上volatile修饰即可
比较volatile锁和sync锁
并发同步锁,sync保证了读写双同步,而volatile只保证了读同步,写不同步,正因为写不同步,所以不能保证原子性
也正因为如此,在大概率的情况下,volatile锁的性能优越于sync锁
使用场景:
由于volatile变量只能保证可见性,在不符合以下两条规则的运算场景中,我们仍然要通过加锁来保证原子性。
运算结果并不依赖变量的当前值(比如 setValue() 方法不依赖当前值,++ 操作符则依赖当前值),或者能够确保只有单一的线程修改变量的值。
变量不需要与其他的状态变量共同参与不变约束。
我的语言整理:
当一个变量在并发的场景中起到了作用,但是了,在这些并发的线程中,只有一个线程可以修改它的值,其他线程只能读取这个值,于是,加上volatile,实现变量值的同步性,当变量值发生变化的时候,所有的线程都可以立即知晓最新结果。同时,由于这个变量值的前后代码的执行顺序不同,可能导致其他线程执行逻辑出错,于是,用volatile禁止指令重排的优化操作