资讯专栏INFORMATION COLUMN

Java多线程之volatile

ZweiZhao / 2149人阅读

摘要:多线程是一个庞大的知识体系,这里对其中的进行一个总结,理清他的来龙去脉。替换重量级锁在中又称为重量级锁,能够保重的几大特性一致性,原子性,可见性。

Java多线程是一个庞大的知识体系,这里对其中的volatile进行一个总结,理清他的来龙去脉。

CPU缓存

要搞懂volatile,首先得了解CPU在运行过程中的存储是如何处理的,其结构如图

CPU会把一些经常使用的数据缓存在cache中,避免每次都去访问较慢的memory。在单线程环境下,如果一个变量的修改都在cache中,自然不会有什么问题,可是在多线程环境中就可能是下面这个图的示意图(单核另当别论)

CPU1 修改了一个变量a存入cache1,但是CPU2 在cache2中看到的a任然是之前的a,所以造成CPU1修改失效,我们来看看示例代码:

import java.util.concurrent.TimeUnit;

public class Counter {
    private static  boolean stop ;
    //private static volatile boolean stop ;
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (!stop) {
                    i++;
                }
            }
        } );
        t.start();

        TimeUnit.MILLISECONDS .sleep(5);
        stop = true;
    }
}

在我的4核笔记本上运行结果:

就一直运行着,没有停止(需要手工停止),这说明在主线程中修改的stop变量后,线程t没有读取到最新的stop的值,还一直是false。

volatile原理

volatile的原理就是,如果CPU1修改了一个变量a,不仅要修改自身的cache,还要同步到memory中去,并且使CPU2的cache中的变量a失效,如果CPU2要读取a,那么就必须到memory中去读取,这样就保证了不同的线程之间对于a的可见性,亦即,无论哪个线程,随时都能获得变量a最新的最新值。
我们来看看示例代码:

import java.util.concurrent.TimeUnit;

public class Counter {
    //private static  boolean stop ;
    private static volatile boolean stop ;
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (!stop) {
                    i++;
                }
            }
        } );
        t.start();

        TimeUnit.MILLISECONDS .sleep(5);
        stop = true;
    }
}

在我的4核笔记本上运行结果:

很快程序就结束了,说明线程t读到了经主线程修改后的stop变量,然后就停止了。

(例子源于《effective Java》)

volatile使用场景 状态标志

就像上面的代码里,把简单地volatile变量作为状态标志,来达成线程之间通讯的目的,省去了用synchronized还要wait,notify或者interrupt的编码麻烦。

替换重量级锁

在Java中synchronized 又称为重量级锁,能够保重JMM的几大特性:一致性,原子性,可见性。但是由于使用了锁操作,在一定程度上会有更高的性能消耗(锁的线程互斥性亦即资源消耗)。而volatile能提供可见性,原子性(单个变量操作,不是a++这种符合操作),所以在读写上,可以用volatile来替换synchronized的读操作,而写操作仍然有synchronized实现,能取得更好的性能。

import java.util.ArrayList;
import java.util.List;

public class Counter1 {

    private class Count11 {
        private  int value;
        public synchronized int getValue() {
            return value;
        }
        public synchronized int increment() {
            return value++;
        }
    }

//    private class Count11 {
//        private volatile int value=0;
//        int getValue() {  return value;    }
//        synchronized int increment() {    return value++;    }
//    }

    public static void main(String[] args) throws Exception {
        Counter1.Count11 count11 = new Counter1().new Count11();
        List threadArrayList = new ArrayList<>();
        final int[] a = {0};
        Long allTime = 0l;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 4; i++) {
            Thread t = new Thread(() -> {
                int b = 0;
                for (int j = 0; j < 10000; j++) {
                    count11.increment();
                    a[0] = count11.getValue();
                }
                for (int j = 0; j < 10000; j++) {
                    b++;
                    a[0] = count11.getValue();
                }
            });
            t.start();
            threadArrayList.add(t);
        }
        for (Thread t : threadArrayList) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        allTime = ((endTime - startTime));
        System.out.println("result: " + a[0] + ", average time: " + (allTime) + "ms");



    }

}

volatile优化结果:

result: 40000, average time: 124ms
result: 40000, average time: 133ms
result: 40000, average time: 141ms
result: 40000, average time: 112ms
result: 40000, average time: 123ms
result: 40000, average time: 143ms
result: 40000, average time: 120ms
result: 40000, average time: 120ms

未优化结果:

result: 40000, average time: 144ms
result: 40000, average time: 150ms
result: 40000, average time: 149ms
result: 40000, average time: 165ms
result: 40000, average time: 134ms
result: 40000, average time: 132ms
result: 40000, average time: 157ms
result: 40000, average time: 138ms
result: 40000, average time: 158ms

可见使用volatile过后效果的确优于只使用synchronized的性能,不过试验中发现有个阈值,如果读取修改次数较小,比如1000以内,只使用synchronized效果略好,存取次数变大以后 volatile的优势才慢慢体现出来(次数达到10000的话,差距就在60ms左右)。

待挖掘

还有很多用法,在将来的学习中,不断总结与挖掘。

联想

无论处于应用的哪一层,优化的思路都是可以相互借鉴的,比如我们做一个服务集群,如果每一个节点都要保存所有用户的session,就很难使得session同步,我们就可以借鉴volatile这种思路,在集群之上搞一个调度器,如果某一个节点修改了一个用户session,就报告给调度器,然后调度器通知其他所有节点修改该用户session。而一般情况下,数据的读写比都比较高,所以这样做就能到达一个很好的性能。

注意事项

引用类型的volatile只在引用本身发生变化时具有可见性,其引用的对象的元素发生变化时不具有可见性

欢迎访问我的个人主页 mageek(mageek.cn)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67040.html

相关文章

  • Java猫说】Java线程内存可见性(下篇)

    摘要:阅读本文约分钟上一次我们说到互斥代码的实现过程,如果有忘记或不清楚的可以去上篇看看。猫说多线程之内存可见性上篇今天我们了解下重排序。 阅读本文约3分钟 上一次我们说到synchronized互斥代码的实现过程,如果有忘记或不清楚的可以去上篇看看。【Java猫说】Java多线程之内存可见性(上篇) 今天我们了解下重排序。 其使代码书写的顺序与实现执行的顺序不同,指令重排序是编译器或处理...

    elliott_hu 评论0 收藏0
  • 慕课网_《细说Java线程内存可见性》学习总结

    时间:2017年07月09日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:无学习源码:https://github.com/zccodere/s... 第一章:课程简介 1-1 课程简介 课程目标和学习内容 共享变量在线程间的可见性 synchronized实现可见性 volatile实现可见性 指令重排序 as-if-seria...

    wupengyu 评论0 收藏0
  • 死磕 java同步系列volatile解析

    摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...

    番茄西红柿 评论0 收藏0
  • 死磕 java同步系列volatile解析

    摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...

    番茄西红柿 评论0 收藏0
  • 死磕 java同步系列volatile解析

    摘要:前半句是指线程内表现为串行的语义,后半句是指指令重排序现象和工作内存和主内存同步延迟现象。关于内存模型的讲解请参考死磕同步系列之。目前国内市面上的关于内存屏障的讲解基本不会超过这三篇文章,包括相关书籍中的介绍。问题 (1)volatile是如何保证可见性的? (2)volatile是如何禁止重排序的? (3)volatile的实现原理? (4)volatile的缺陷? 简介 volatile...

    kviccn 评论0 收藏0

发表评论

0条评论

阅读需要支付1元查看
<