page contents

volatile是否能保证数组中元素的可见性

Pack 发布于 2020-01-10 16:10
阅读 547
收藏 0

今天看到一个很有意思的问题,自己测试了一下,对结果有一些疑惑,特来请各位老哥帮忙看看。

问题一:


public class VolatileTest implements Runnable {

    private B b;

//    private volatile  B b;

    public VolatileTest(B b) {

        this.b = b;

    }

    @Override

    public void run() {

        while (b.getFlag()){

            System.out.println(b.getFlag());//①

        }

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

    }

    public void stop(){

        b.setFlag(false);

    }


    public static void main(String[] args) throws InterruptedException {

        B b = new B();

        VolatileTest test = new VolatileTest(b);

        Thread t = new Thread(test);

        t.start();

        System.out.println("启动线程");

        Thread.sleep(1000);

        test.stop();

        Thread.sleep(1000);

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

    }

}


public class B {

    private Boolean flag = true;


    public Boolean getFlag() {

        return flag;

    }


    public void setFlag(Boolean flag) {

        this.flag = flag;

    }

}

情况一: 不注释①的代码,b不需要volatile修饰也可以线程终止

attachments-2020-01-HEz0Rf5D5e18314f51c09.png

情况二: 注释掉①的代码,b必须要volatile修饰才能终止线程

疑问: 情况一的原因?


问题二:


public class VolatileTest1 implements Runnable {

    private /*volatile*/ A a;


    public VolatileTest1(A a) {

        this.a = a;

    }


    @Override

    public void run() {

        ASub sub = a.getaSub();

        while (sub.getFlag()){

//            System.out.println(sub.getFlag());//①

        }

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

    }


    public static void main(String[] args) throws InterruptedException {

        A a = new A();

        ASub sub = new ASub();

        a.setaSub(sub);

        VolatileTest1 test = new VolatileTest1(a);

        Thread t = new Thread(test);

        t.start();

        System.out.println("启动线程");

        Thread.sleep(1000);

        sub.setFlag(false);

        Thread.sleep(1000);

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

    }

}


public class A {

    private ASub aSub;


    public ASub getaSub() {

        return aSub;

    }


    public void setaSub(ASub aSub) {

        this.aSub = aSub;

    }

}


public class ASub {

    private Boolean flag = true;


    public Boolean getFlag() {

        return flag;

    }


    public void setFlag(Boolean flag) {

        this.flag = flag;

    }

}

情况一: 注释掉①处的代码,加不加volatile修饰a 线程都无法停止。

情况二: 不注释掉①出的代码,加不加volatile修饰a 线程都可以停止。

那么问题来了?

疑问: 情况一是什么原因?情况二又是什么原因?

164
Pack
Pack

volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略。


volatile 的特性

保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)

禁止进行指令重排序。(实现有序性)

volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。

情况一主线程修改了flag,但是线程a不能立即获取到flag的最新值,所以线程无法立即停止


情况二在线程a中显式调用获取flag值,当然就可以停止线程

请先 登录 后评论