java 中轻量级volatile同步字段解释

导读:本篇文章讲解 java 中轻量级volatile同步字段解释,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

前言:

最近在整理多线程的一些用法,其中有涉及到 volatile 关键字,没有深入研究,只是简单的验证了一下,这里记录一下心得。

一. volatile 是轻量级的

为啥一定要强调轻量级?

1. volatile 只能修饰变量

2. 保证不同线程操作时变量的可见性,即一个线程修改了变量,另一个线程立马可见

3. 不能确保线程同步(这个下面会举例说明)

二. 变量可见性验证

几乎所有的博客都会举下面这个例子说明


public class VolatileTest extends Thread {
    boolean flag = false;
    int i = 0;

    public void run() {
        while (!flag) {   // 如果为true会马上结束循环
            i++;
        }
    }

    public static void main(String[] args) throws Exception {
        VolatileTest vt = new VolatileTest();
        vt.start();
        Thread.sleep(10);
        System.out.println(vt.isAlive());
        vt.flag = true;   //设置为true,意图停止循环
        System.out.println("store:" + vt.i);
        Thread.sleep(10);
        System.out.println(vt.isAlive());
    }
}

打印一下结果:

true
store:7517191
true

进程没有结束,并且陷入死循环,跟想要的结果相去甚远。

这里其实涉及到java内存模型的问题,有点复杂,这里简单说一下结论:

上述代码中的main主线程启动了 vt 线程, vt 线程启动后设置 flag为 false,vt 线程在执行的时候flag值是不会自动更新的(vt.flag 就没有意义了),所以flag一直是false。


public class VolatileTest extends Thread {
    volatile boolean flag = false;
    int i = 0;

    public void run() {
        while (!flag) {   // 如果为true会马上结束循环
            i++;
        }
    }

    public static void main(String[] args) throws Exception {
        VolatileTest vt = new VolatileTest();
        vt.start();
        Thread.sleep(10);
        System.out.println(vt.isAlive());
        vt.flag = true;   //设置为true,意图停止循环
        System.out.println("store:" + vt.i);
        Thread.sleep(10);
        System.out.println(vt.isAlive());
    }
}

加上 volatile 关键字以后,线程在使用 flag 的时候会主动去更新值,这样 vt.flag = true 就会生效,vt 线程就退出循环逐步结束,输出结果如下

true
store:7537387
false

三. volatile 不能确保线程同步

大家可能会觉得,既然 volatile 可以保证变量是及时可见的,那为啥又不能保证线程同步?别急,这里用事实说话

public class VolatileTestSyn {
    public volatile int inc = 0;

    public void increase() {
        inc++;
    }

    public static void main(String[] args) {
        final VolatileTestSyn test = new VolatileTestSyn();
        for (int i = 0; i < 5; i++) {
            new Thread() {
                public void run() {
                    for (int j = 0; j < 1000; j++)
                        test.increase();
                }
            }.start();
        }
        while (Thread.activeCount() > 2)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println("result:"+test.inc);
    }
}

打印结果(机器性能高的话可能还是5000,可以适当调大循环次数或线程个数):

4658

如果线程是同步的,那结果肯定是5000没跑,结果却不是,为啥?

多个线程同时操作 inc,每次取到的 inc 都是计算后写到内存的最新值,按道理不应该出错;但是,假如 A 线程启动取得的 inc 是100,这时候 B 线程也启动取得 inc 100;A 线程执行 +1 操作把 101 写到内存,但是这时候 B 线程短暂暂停(注意,B线程已经取得 inc 为 100,B线程不会再更新 inc 了); C 线程启动获取 inc 为101,执行 +1 操作,并把102 写到内存,而后很快 B 线程也执行了 +1 操作,把 101 写到内存覆盖了 C线程的结果;这时候 D线程启动获取的 inc 是 101……..这就解释了为啥上面结果不是 5000。(这块涉及 Java 内存模型,想深究的同学可以研究一下)

所以这时候就需要给 increase() 方法加一个 synchronized ,使得方法在多线程单一持有,再也不会出现上面的问题了。

四. 参考博客

Java并发编程:volatile关键字解析

Java中Volatile关键字详解

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/16523.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!