首页 > 其他分享 >volatile关键字

volatile关键字

时间:2023-04-08 14:34:56浏览次数:51  
标签:屏障 关键字 指令 volatile 内存 重排 线程

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

  1. 内存可见性 (保证可见性)
  2. 不保证原子性
  3. 禁止指令重排 (保证有序性)

可见性

  • volatile修饰的共享变量有如下特点
    • 线程中读取这个变量时,每次都会读取主内存中最新的值,然后将其复制到工作内存
    • 线程中修改了工作内存中变量的副本,修改之后会立即刷新到主内存

有序性

  • 计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排

    • 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行的指令
    • 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
    • 处理器在进行重排序时必须要考虑指令之间的 数据依赖性
    • 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一执行是无法确定的,结果无法预测
  • 是通过内存屏障来实现禁止指令重排

    • 内存屏障其实就是一种IVM指令,java内存模型的重排规则会要求java编译器在生成JVM指令时插入特定的内存屏障指令
      • 内存屏障之前的所有写操作都要写回主内存
      • 内存屏障之后的所有读操作都能获取 内存屏障之前所有写操作的最新结果
    • 写屏障
      • 告诉处理器在写屏障之前将所有存储在缓存中的数据同步到主内存,也就是说当看到 store屏障指令,就必须把该指令之前的所有写入指令执行完毕才能继续往下执行
    • 读屏障
      • 处理器在读屏障之后的读操作,都在读屏障之后执行。也就是说在Load屏障指令之后,就能保证后面读取的数据一定是最新的
    • 细分屏障类型(调用底层,底层的分类)
      • LoadLoad
      • StoreStore
      • LoadStore
      • StoreLoad

volatile使用场景

  • 单一赋值,如boolean、或者int
  • 状态标志,判断业务是否结束
  • 读多写少的场景,读不用加锁,写操作需要加锁
  • 单例模式下的双重检查锁,利用它的禁止指令重排

代码证明

  • 可见性
    • 工作线程对变量的更改在写回主内存,并将新修改的值同步给其他工作线程。
public class VolatileModel {

    volatile int model = 0;

    private void updateModel(){
        model = 60;
    }

    public static void main(String[] args) {

        VolatileModel volatileModel = new VolatileModel();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " come in.");

            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            volatileModel.updateModel();
            System.out.println(Thread.currentThread().getName() + " data:" + volatileModel.model);
        }, "AAA").start();

        while(volatileModel.model == 0){

        }
        System.out.println(Thread.currentThread().getName() + " data:" + volatileModel.model);

    }
}

  • 不保证原子性

public class Data {

    volatile int n = 0;

    public void add(){
        n++;
    }

    //result: 结果小于20000
    //原因:由于20个线程在进行N++的操作时,都是在自己的工作内存中完成,然后在写会主内存的
    // n++是非原子性的,javac -p查看class文件对应的 是3个操作,线程在执行这3个操作会出现插队情况,就会出现原本+1后写会主内存的值 实际上已经被其他线程+1过了
    public static void main(String[] args) {
        Data d = new Data();

        for(int i = 0;i< 20;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int j = 0;j < 1000;j++){
                        d.add();
                    }
                }
            }).start();
        }

        while (Thread.activeCount() > 2){
            Thread.yield();
        }

        System.out.println(d.n);
    }
}

  • 解决volatile原子性问题

    • synchronized关键字
    • Lock锁
    • AtomicInteger类
      • cas实现,自旋锁
  • 有序性

标签:屏障,关键字,指令,volatile,内存,重排,线程
From: https://www.cnblogs.com/liyong888/p/17298505.html

相关文章

  • 知识回顾——final关键字
    一.什么时候使用final关键字1.被final修饰的类不能被继承,所以当我们希望某类不能被继承时,就是用final修饰此类。2.当不希望父类的某个方法不被子类覆写(Override),可以用final修饰此方法。3.当不希望类的某个属性的值被修改,可以用final修饰。4.当不希望某个局部......
  • static关键字,main方法,代码块,final关键字
    static关键字,main()方法,代码块,final关键字static关键字的使用:static:静态的static可以修饰:属性、方法、代码块、内部类使用static修饰的变量:静态变量(类变量)3.1属性:实例变量:我们创建了多个类的对象,每个对象都拥有一套独立的类的非静态属性,当修改其中一个对象的非静......
  • 序列化与transient关键字
    什么是序列化?看到别人说的感觉非常的好。简单来说,我们把对象从内存中变成可存储或传输的过程称之为序列化。 为什么要用序列化?简单来说,我们把对象从内存中变成可存储或传输的过程称之为序列化。 序列化的应用1)将数据转换成二进制流的形式,用于数......
  • SQL语句的其他关键字
    目录数据准备编写SQL语句小技巧查询关键字之where筛选查询关键字之groupby分组查询关键字之having过滤查询关键字之distinct去重查询关键字之orderby排序查询关键字之limit分页查询关键字之regexp正则表达式多表查询的思路数据准备#数据准备createtableemp(idintpri......
  • super关键字
    super关键字基本介绍super代表父类的引用,可以引用方法,属性以及构造器基本语法访问父类的属性,但不能访问private属性,案例:super.属性名;访问父类的方法,但不能访问private方法,案例:super.方法名(参数列表);访问父类的构造器,super.(参数列表);super带来的便利/细节调用父类构......
  • super关键字
    super关键字基本介绍super代表父类的引用,可以引用方法,属性以及构造器基本语法访问父类的属性,但不能访问private属性,案例:super.属性名;访问父类的方法,但不能访问private方法,案例:super.方法名(参数列表);访问父类的构造器,super.(参数列表);super带来的便利/细节调用父类构......
  • 1688关键字搜索新品数据API接口(item_search_new-按关键字搜索新品数据)
    1688关键字搜索新品数据API接口(item_search_new-按关键字搜索新品数据)代码接口教程如下:公共参数名称类型必须描述key String 是 调用key(必须以GET方式拼接在URL中)secret String 是 调用密钥api_name String 是 API接口名称(包括在请求地址中)[item_search,item_get,item_search......
  • 自动化测试当中的三大设计技巧:PO设计思想,数据驱动及关键字驱动
    大家好,我是洋子。当我们以离线脚本的形式编写了大量的自动化测试代码后,很容易发现以下常见问题:(1)对于UI自动化,当UI层的元素发生改变,需要修改所有相关的case,工作量巨大(2)代码难以扩展,每次想新增一个自动化case就要写新的逻辑,补充新的代码(3)代码可读性差,代码冗余,存在大量重复代码或者......
  • CAS & volatile
    1.CAS1.1问题在JDK5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁锁机制存在以下问题:(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。(3)如果一个优先级高的线程等待一个优......
  • 注释/关键字/常量/数据类型/变量/标识符/类型转换
                                        ......