美文网首页
JUC-volatile

JUC-volatile

作者: GIT提交不上 | 来源:发表于2020-02-24 18:10 被阅读0次

      volatile:Java虚拟机提供的轻量级的同步机制(synchronized)

    • 保证可见性(一个线程修改了主内存的值,其他线程立即得知改变)
    • 不保证原子性
    • 禁止指令重排

      JMM(Java内存模型),工作内存中存储着主内存的变量副本拷贝。工作内存是每个线程的私有区域,JMM规定所有变量都存储在主内存,主内存是共享内存区域,所有线程均可访问,线程对变量的操作(读取赋值等)只能在工作内存操作,首先将变量从主内存拷贝到自己的工作内存空间,然后对变量操作,操作完成后将变量写回主内存,不能直接操作主内存的变量。(三大特性-1.可见性2.原子性3.有序性)

    JMM模型

      volatile可见性示例代码:

    /**
     * @author luffy
     */
    public class Main {
        public static void main(String[] args) {
            Data data = new Data();
            new Thread(() ->{
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                data.dataChange();
            },"AAA").start();
            while (data.number == 0){
    
            }
            System.out.println("end "+ data.number);
        }
    }
    class Data{
        int number = 0;   //可见性-volatile int number = 0;  
        void dataChange(){
            this.number = 50;
        }
    }
    

      原子性:不可分割,不可中断,要么全部执行成功,要么全部失败,保证数据的完整一致性。volatile不保证原子性示例代码:

    /**
     * @author luffy
     */
    public class Main {
        public static void main(String[] args) {
            Data data = new Data();
            for(int i =0 ;i< 20;i++){
                new Thread(()->{
                    for(int j=0;j<1000;j++){
                        data.dataAdd();
                    }
                },String.valueOf(i)).start();
            }
            while (Thread.activeCount() > 2){
                Thread.yield();
            }
            System.out.println(data.number);
        }
    }
    class Data{
        int number = 0 ;
        void dataAdd(){
            number++;
        }
    }
    
    number++汇编指令

      使用Atomic原子类保证原子性(CAS),示例代码:

    /**
     * @author luffy
     */
    public class Main {
        public static void main(String[] args) {
            Data data = new Data();
            for(int i =0 ;i< 20;i++){
                new Thread(()->{
                    for(int j=0;j<1000;j++){
                        data.dataAdd();
                    }
                },String.valueOf(i)).start();
            }
            while (Thread.activeCount() > 2){
                Thread.yield();
            }
            System.out.println(data.atomicInteger);
        }
    }
    class Data{
        AtomicInteger atomicInteger = new AtomicInteger();
        void dataAdd(){
            atomicInteger.getAndIncrement();
        }
    }
    

      指令重排:源代码->编译器优化的重排->指令并行的重排->内存系统的重排->最终执行的指令(多线程)。指令重排需要考虑指令之间的数据依赖性。
      通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化(禁止重排);强制刷出各种CPU的缓存数据(可见性)。

    /**
     * @author luffy
     **/
    public class Test {
        int number = 0;   //volatile修饰
        boolean flag = false;
    
        public void method1(){
            this.number = 1;
            this.flag = true;    //指令可能重排,多线程中变量一致性无法保证
        }
        public void method2(){
            if(flag){
                number+=5;
                System.out.println(number);
            }
        }
    }
    

      volatile用例:单例模式-DCL(双端检测)机制,示例如下:

    /**
     * @author luffy
     **/
    public class Test {
        private static Test instance = null;  //可能存在线程安全性问题,用volatile修饰
        private Test(){
            System.out.println(Thread.currentThread().getName()+"test 构造方法!");
        }
        public static Test getInstance(){
            if (instance == null){
                synchronized (Test.class){
                    if(instance == null){
                        instance =  new Test();
                    }
                }
            }
            return instance;
        }
        public static void main(String[] args){
            for(int i =0 ;i< 10;i++){
                new Thread(()->{
                    Test.getInstance();
                },String.valueOf(i)).start();
            }
        }
    }
    
    instance =  new Test();
    //读取到的instance不为null,instance引用对象可能没有完成初始化
    memory = allocate();  // 1 分配对象内存空间
    instance(memory);  // 2 初始化对象
    instance = memory;  // 3 设置instance指向分配的内存地址,此时!=null
    步骤2和3不存在数据依赖关系,可能存在指令重排
    

    相关文章

      网友评论

          本文标题:JUC-volatile

          本文链接:https://www.haomeiwen.com/subject/gnsvqhtx.html