volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?

1 个回答

  • hello
    用眼睛是看不到事物的本质的,只有心灵才能够洞察一切。
    当线程进行一个volatile变量的写操作时,JIT编译器生成的汇编指令会在写操作的指令后面加上一个“lock”指令。 Java代码如下: ```java instance = new Singleton(); // instance是volatile变量 转变成汇编代码,如下。 0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp); ``` “lock”有三个作用: 1.将当前CPU缓存行的数据会写回到系统内存。 2.这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效。 3.确保指令重排序时,内存屏障前的指令不会排到后面去,内存屏障后的指令不会排到前面去。 可见性可以理解为一个线程的写操作可以立即被其他线程得知。为了提高CPU处理速度,CPU一般不直接与内存进行通信,而是将系统内存的数据读到内部缓存,再进行操作。对于普通的变量,修改完不知道何时会更新到系统内存。但是如果是对volatile修饰的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在的缓存行的数据立即写回到系统内存。但是即便写回到系统内存,其他CPU中的缓存行数据还是旧的,为了保证数据一致性,其他CPU会嗅探在总线上传播的数据来检查自己的缓存行的值是否过期,当CPU发现缓存行对应的内存地址被修改,那么就会将当前缓存行设置为无效,下次当CPU对这个缓存行上的数据进行修改时,会重新从系统内存中把数据读到处理器缓存里。 #### 使用场景 ##### 读写锁 如果需要实现一个读写锁,每次只能一个线程去写数据,但是有多个线程来读数据,就synchronized同步锁来对set方法加锁,get方法不加锁, 使用volatile来修饰变量,保证内存可见性,不然多个线程可能会在变量修改后还读到一个旧值。 ```java volatile Integer a; //可以实现一写多读的场景,保证并发修改数据时的正确。 set(Integer c) { synchronized(this.a) { this.a = c; } } get() { return a; } ``` **状态位** 用于做状态位标志,如果多个线程去需要根据一个状态位来执行一些操作,使用volatile修饰可以保证内存可见性。 用于单例模式用于保证内存可见性,以及防止指令重排序。
    发布于 2022-02-08 14:40 •IP属地 甘肃
    举报
    评论
    收藏