验证volatile两个特性

137 阅读2分钟

可见性

可见性:被它修饰的变量对所有线程都可见,当一个线程修改了这个值,新值对于其它线程来说是可以立即得知的。测试代码如下,当flag属性未被volatile修饰时,main方法不会停止,并未在控制台输出“主线程结束”,当使用volatile修饰时,这种情况没有发生,可见volatile具备可见性。

public class Main {

    private volatile boolean flag = true;

    public void changeFlag() {
        flag = false;
    }

    public static void main(String[] args) {
        Main main = new Main();
        new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            main.changeFlag();
            System.out.println("值已经改变" + main.flag);
        }).start();
        while (main.flag) {

        }
        System.out.println("主线程结束");
    }

}

验证禁止指令重排序

运行如下代码,可以看出线程ThreadA中执行a,b,c,d,e,f,g赋值语句不一定按照顺序执行,对它们添加volatile关键字之后,这种情况就不再发生,可见volatile确实能禁止指令重排序。

public class VolatileTest {
    public static void main(String[] args) {
        VolatileTest main = new VolatileTest();
        for (int i = 0; i < 500000; i++) {
            main.test();
        }
    }

    public synchronized void test() {
        Test test = new Test();
        ThreadA t1 = new ThreadA(test);
        ThreadB t2 = new ThreadB(test);
        t1.start();
        t2.start();
    }

    /**
     * 测试volatile时修改为如下代码
     * public volatile int a = 0;
     * public volatile int b = 0;
     * public volatile int c = 0;
     * public volatile int d = 0;
     * public volatile int e = 0;
     * public volatile int f = 0;
     * public volatile int g = 0;
     * public volatile int h = 0;
     */
    static class Test {
        public int a = 0;
        public int b = 0;
        public int c = 0;
        public int d = 0;
        public int e = 0;
        public int f = 0;
        public int g = 0;
        public int h = 0;
    }

    static class ThreadA extends Thread {
        Test test;

        public ThreadA(Test test) {
            this.test = test;
        }

        @Override
        public void run() {
            test.a = 1;
            test.b = 1;
            test.c = 1;
            test.d = 1;
            test.e = 1;
            test.f = 1;
            test.g = 1;
            test.h = 1;
        }

    }

    static class ThreadB extends Thread {
        Test test;

        public ThreadB(Test test) {
            this.test = test;
        }

        @Override
        public void run() {
            if (test.b == 1 && test.a == 0) {
                System.out.println("b=1");
            }
            if (test.c == 1 && (test.a == 0 || test.b == 0)) {
                System.out.println("c=1");
            }
            if (test.d == 1 && (test.a == 0 || test.b == 0 || test.c == 0)) {
                System.out.println("d=1");
            }
            if (test.e == 1 && (test.a == 0 || test.b == 0 || test.c == 0 || test.d == 0)) {
                System.out.println("e=1");
            }
            if (test.f == 1 && (test.a == 0 || test.b == 0 || test.c == 0 || test.d == 0 || test.e == 0)) {
                System.out.println("f=1");
            }
            if (test.g == 1 && (test.a == 0 || test.b == 0 || test.c == 0 || test.d == 0 || test.e == 0 || test.f == 0)) {
                System.out.println("g=1");
            }
            if (test.h == 1 && (test.a == 0 || test.b == 0 || test.c == 0 || test.d == 0 || test.e == 0 || test.f == 0 || test.g == 0)) {
                System.out.println("h=1");
            }
        }

    }

}