(七)JUC - volatile

1. volatile变量 两大特性

可见性、有序性(排序要求:禁止重排)

3f9d3036bdeb49b4b97dba3209ff0ddc.png

写,线程本地内存中的共享变量值立即刷新回主内存

读,线程本地内存设置为无效,重新回到主内存中读取最新共享变量

 

Qvolatile凭什么可以保证可见性和有序性?

--- 内存屏障 Memory Barrier

 

 2. 四大内存屏障(面试重点

“人墙”

内存屏障是什么?

bc1f8505c2484407a9c9a0c03b7ea578.png

写后读

d1e862806d5e47aeb57cd515780f4309.png

 

内存屏障分类:

粗分2种

读屏障(Load Barrier)

写屏障(Store Barrier)

c4662927f25241559bb2409beca6ec8c.png

细分4种

824b0b18972841fdbea91f2dbbac02cc.png

 

Unsafe.class -> Unsafe.java -> Unsafe.cpp -> OrderAccess.hpp -> orderAccess_linux_x86.inline.hpp

ab881c096f9d409f957ca270a7b4715b.png

 

3. Volatile读写屏障插入策略

什么叫保证有序性?--禁重排 --通过内存屏障禁重排

f83d5924541b47a2a4c90de8c2008d04.png

1)volatile读

bc9d5580abd8420bbff1b9bff5177b7a.png

2volatile

ca6cfa66931947d08667d1fa692c519c.png

 

4. volatile可见性案例

package com.bilibili.volatiles;

import java.util.concurrent.TimeUnit;

/**
 * volatile 可见性
 * 
 * @author baiyuehua
 *
 */
public class VolatileSeeDemo {
    
//    static boolean flag = true;  // 程序无法停止
    static volatile boolean flag = true;  // 程序可以停止
    
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t---come in");
            while (flag) {
                
            }
            System.out.println(Thread.currentThread().getName()+"\t---flag被设置为false,程序停止");
        }, "t1").start();;
        
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        flag = false;
        
        System.out.println(Thread.currentThread().getName()+"\t---修改完成flag: "+flag);
    }
}

d9c92cb2481f460482a9482d5795e727.png

volatile变量的读写过程

c2b21e1a967f4b8eaa118d45439def9b.png

1c4f38e53d914b759401f190d3ee8496.png

 

5. volatile无原子性案例

volatile变量的复合操作不具有原子性。egnumber++

 

不保证原子性,写丢失问题

d4236e812b364b03a4626889a4f46306.png

 

结论:volatile变量不适合参与到依赖当前值的运算       ---《深入理解Java虚拟机》

0c902e8f77584f428a287fa7a0e47a98.png

 

面试回答: JVM的字节码,i++分为三步,间隙期不同步非原子操作。

对于volatile变量,JVM只是保证从主内存加载到线程工作内存的值是最新的,也只是数据加载时时最新的。如果第二个线程在第一个线程读取旧值和写回新值期间读取 i 的域值,也就造成了线程安全问题。

045092355f3d480b836c24e5d99c1c06.png

6. volatile禁重排案例

  • 不存在数据依赖关系,可以重排序;
  • 存在数据依赖关系,不可以重排序。

1)重排序的分类和执行流程:

34b5ebfc2ab94d2d8bd466b9477a4d1a.png

2)数据依赖性:

若两个操作访问同一变量,且这两个操作中有一个为写操作,此时两操作间就存在数据依赖性。

3)重排序,程序执行结果改变,三种情况:

dc4d8b4dec8740078ef1611940567788.png

 

7. volatile适用场景

1单一赋值可以,but含复合运算赋值不可以(i++之类)

2)状态标志,判断业务是否结束

a970ad5a80564d68a49a53f66d8599f1.png

3)开销较低的读,写锁策略

bb17f3c82eb841ff981db50a5c59f260.png

4)DCL双端锁的发布

高并发下的单例模式

问题:

ebed27bc40684b62bbb3481a202cd15c.png

单线程看问题:

422e41f0ff3c4e2abdb4ef8ac1e69a76.png

多线程看问题:

6677a7e1b3984d2cb8f82f7b7284b459.png

1f333cf6ebc2410c89cf997606218df9.png

解决:

volatile修饰

8b35395f2598449b9a524bac1e0aab98.png

 

8. volatile小总结

1)可见性

26bd7f38d963458e948adaa8c675117a.png

2)非原子性

3)禁重排

写指令

be15eced14024ba4831727c1a60b1661.png

读指令

f85f8cc0de424a2db16b37ad69aecb7d.png

 

Q:凭什么我们java写了一个volatile关键字,系统底层就加入内存屏障?两者是怎么关联上的?

1a109764763142b1ad82309a62d31eaa.png

 

Q:内存屏障是什么?

5b5d54123f1e48b39bcc623c7e6f81fa.png

 

Q:内存屏障能干嘛?

736e90b24cd14430bfddece02f736025.png

 

内存屏障的四大指令:

3句话总结:

3c4820043f354020b69e33d87273c9da.png

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值