• 关于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 禁止指令重排(有序性)

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

  • 相关阅读:
    日期和时间模块
    批处理bat文件dos命令实现文件的解压缩
    批处理bat文件dos命令复制文件
    dos命令临时和永久设置环境变量方法
    [转]NHibernate之旅(13):初探立即加载机制
    [转]NHibernate之旅(12):初探延迟加载机制
    [转]NHibernate之旅(11):探索多对多关系及其关联查询
    [转]NHibernate之旅(10):探索父子(一对多)关联查询
    [转]NHibernate之旅(9):探索父子关系(一对多关系)
    [转]NHibernate之旅(8):巧用组件之依赖对象
  • 原文地址:https://www.cnblogs.com/zhangyuanbo/p/14185041.html
Copyright © 2020-2023  润新知