zoukankan      html  css  js  c++  java
  • 关于volatile

    1.volatile

    volatile是java虚拟机提供的轻量级同步机制

    2.特性

    保证可见性,不保证原子性,禁止指令重排(有序性)

    2.1 可见性

    首先要知道JMM,就是java内存模型(可见性、原子性、有序性)
    这是一个抽象概念;内存分为主内存和工作内存。主内存主要存放共享变量等等,用于数据共享的
    而工作内存是线程操作资源的一个区域,每个线程都有自己的工作内存。
    资源的操作流程主要分为以下三步:

      1.线程从主内存中copy取出需要操作的资源  
      2.线程操作资源  
      3.写回主内存(在CAS中,需要比较此时主内存的内容和之前拿到的内容是否相同)  
    

    而在线程写回主内存后,需要通知其他线程这个共享变量已经被修改,如果修改了,其他线程就重
    新从主内存中获取,这就叫做可见性

    下面是一段代码演示:

    /**
     * volatile的可见性
     */
    public class TestVolatile {
        public static void main(String[] args) {
            SourceTest sourceTest = new SourceTest();
    
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName());
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {e.printStackTrace();}
                sourceTest.addNum();
                System.out.println(Thread.currentThread().getName() + "修改a后: " + sourceTest.a);
            },"sub thread").start();
    
            while (sourceTest.a == 0){
                //进入循环
                System.out.println("主线程进入循环取得a的值:" + sourceTest.a);
            }
    
            System.out.println(Thread.currentThread().getName() + "得到a的值: " + sourceTest.a);
        }
    }
    
    class SourceTest{
        //int a = 0;//此时不加volatile主线程进入死循环,进程无法结束
        volatile int a = 0;
    
        void addNum(){
            a = 60;
        }
    }
    

    输出:
    ...
    主线程进入循环取得a的值:0
    主线程进入循环取得a的值:0
    sub thread修改a后: 60
    main得到a的值: 60

    2.2 不保证原子性

    原子性:不可分割,完整性,即线程在处理某个业务时,中间不允许被加塞

    public class TestAtomic {
        public static void main(String[] args) throws InterruptedException {
            DataSource dataSource = new DataSource();
    
            for (int i = 1; i <= 20; i++) {
                new Thread(() -> {
                    for (int j = 1; j <= 1000 ; j++) {
                        dataSource.getAndIncre();
                    }
                },String.valueOf(i)).start();
            }
    
            //等待以上线程执行完成,main线程取值
            //TimeUnit.SECONDS.sleep(5);
            while (Thread.activeCount() > 2){
                Thread.yield();
            }
    
            System.out.println(Thread.currentThread().getName() + "	 最终number: " + dataSource.number);
        }
    }
    
    class DataSource{
        volatile int number = 0;
    
        void getAndIncre(){
            number++;
        }
    }
    

    输出结果:
    main 最终number: 19081

    以此可看出volatile不保证原子性(i++线程不安全=>主要是i++先自增,再返回自增之前的值,导致某时通知判断错误)

    解决无法保证原子性

    1.加sync
    2.AtomicInteger

    AtomicInteger atomicInteger = new AtomicInteger();
    
        void getAndIncre(){
            //number++;
            atomicInteger.getAndIncrement();
        }
    

    2.3 禁止指令重排(有序性)

    计算机在执行程序时,为了提高性能,编译器和处理器会对指令进行重排:
    源代码>(编译器优化的重排>指令并行的重排>内存系统的重排)>最终执行的指令
    在单线程环境中,可以确保最终执行的结果与代码顺序一致
    处理器在重排时必须考虑指令之间的数据依赖性
    而多线程环境中线程交替执行,两个线程使用的变量就无法确定能保持一致性

  • 相关阅读:
    如何运用NLP技巧处理负面情绪
    寻找出路:企业高层面临的困境及对策
    星雨行
    职业发展:从基层到高层的“突破规律”
    老总必看:如何培养自己的“领袖气质”
    成功领导者的“整合性思维”,自己如何培养?
    杨晓芳(帮别人名字作诗)
    别舞动我的情觞
    卖月光
    创业如何找钱:越简单模式越容易成功
  • 原文地址:https://www.cnblogs.com/zhangyuanbo/p/14185041.html
Copyright © 2011-2022 走看看