主要做用:java
1.保证数据之间的可见性。segmentfault
2.禁止指令重排序。多线程
2.作个小的测试jvm
public class VolatileTest implements Runnable { //当为false时线程结束 private static /*volatile*/ boolean flag = true; private static int value = 100; @Override public void run() { // TODO Auto-generated method stub while(flag) { value++; //System.out.println(value);//能够取消注释试一试 } System.out.println(Thread.currentThread().getName()+"结束"); } public static void main(String[] args) throws InterruptedException { new Thread(new VolatileTest() ).start(); Thread.sleep(1000); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub flag = false; System.out.println(Thread.currentThread().getName()+"结束"); } }).start(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"结束"); System.out.println("flag="+flag); } }
结果:ide
Thread-1结束 main结束 flag=false
咱们能够发现,第二个线程将flag改为false,可是第一个线程并无中止运行。测试
1.多线程内存模型优化
3.为何?spa
从第一幅图咱们能够看出,各一个线程都有一个工做内存,线程运行时,他会从主内存读取数据到工做内存,而后在使用工做内存,执行完在save到工做内存.可是其余线程感知不到主内存的变化,不知道主内存的flag变成了false,因此没有更新本身的工做空间中的flag(由于没有操做让他去主内存读取数据),致使flag为true,因此循环没法终止.线程
4.volatile的做用:强制让其读取主内存,而不是工做空间,多个线程使用的为同一个空间,就保证了可见性.code
5.volatile保证了可见性可是却没有保证原子性.须要其余操做来实现。
若是将flag
的volatile添加上。
结果:
Thread-1结束 Thread-0结束 main结束 flag=false
volatile禁止jvm和处理器对volatile修饰的指令进行重排序,可是修饰符前和后的指令没有明确的规定。
何为重排序:
在单线程下:jvm为了提升执行的效率,会对咱们的代码进行优化,对咱们的指令进行位置的更换,可是更换有个前提,就是在单线程下逻辑不变,好比:
int a = 1; int b = 2; int c = a + b; //更换为 int b = 2; int a = 1; int c = a + b;
这种更换不会改变结果(单线程下)。
同时对于cpu来讲,为了知足效率问题,也会对咱们的指令进行重排序,提升cpu流水线
的效率。
重排序规则:
int a = 1; int b = 2; volatile int c = 3; int d = 4; int f = 6;
volatile能够禁止重排序,可是只针对修饰的命令,对于上面的程序,a,b没有修饰,因此,a,b能够重排序,同时d,f也能够,可是ab和df是不会进行重排序的,由于volatile生成内存屏障
。
(1)volatile写操做前面插入一个StoreStore屏障。确保在进行volatile写以前前面的全部普通的写操做都已经刷新到了内存。
(2)volatile写操做后面插入一个StoreLoad屏障。避免volatile写操做与后面可能存在的volatile读写操做发生重排序。
(3)volatile读操做后面插入一个LoadLoad屏障。避免volatile读操做和后面普通的读操做进行重排序。
(4)volatile读操做后面插入一个LoadStore屏障。避免volatile读操做和后面普通的写操做进行重排序。
简单来讲,volatile修饰的前面的指令不会和后面的指令进行重排序,同时运行volatile修饰时,前面的代码所有执行完毕。
举个重排序的例子:
public class Disorder { private static int x = 0, y = 0; private static int a = 0, b = 0; public static void main(String[] args) throws InterruptedException { int count = 0; long start = System.currentTimeMillis(); while(true){ count++; x = 0; y = 0; a = 0; b = 0; Thread one = new Thread(() -> { a = 1; x = b; }); Thread other = new Thread(() -> { b = 1; y = a; }); one.start();other.start(); one.join();other.join(); if (x == 0 && y ==0){ long end = System.currentTimeMillis(); System.out.println("程序运行次数:"+count); System.out.println("程序耗时:"+(end-start)); break; } } } }
加入,咱们认为程序是一行一行执行的,即顺序不会发生改变。
状况1(one) | 状况1(other) | 状况2(one) | 状况2(other) | 状况3(one) | 状况3(other) | |
---|---|---|---|---|---|---|
a = 1 | a = 1 | a = 1 | ||||
x = b | b = 1 | b = 1 | ||||
b = 1 | x = b | y = a | ||||
y = a | y = a | x = b | ||||
结果 | a=1 x=0 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
状况4(one) | 状况4(other) | 状况5(one) | 状况5(other) | 状况6(one) | 状况6(other) | |
b = 1 | b = 1 | b = 1 | ||||
a = 1 | a = 1 | y = a | ||||
x = b | y = a | a = 1 | ||||
y = a | x = b | x = b | ||||
结果 | a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=0 |
按照上述排序,能够看出永远是不会出现x =0 和y = 0同时存在的状况出现,那么这个程序就没有结果。
可是运行程序:
程序运行次数:5130 程序耗时:2453
程序成功退出,表示出现了xy同时为零的状况。这表示某个线程的指令没有按照顺序执行,顺序被打乱了。