• 面试题:volatile能否保证线程安全


    目的

    方便后续自己复习方便, 也是一次学习的记录.

    资源

    B站的一个讲高频面试题的一个学习视频

    如何理解线程安全

    参考: 什么是线程安全

    线程安全要考虑的3个方面

    可见性

    一个线程对共享变量修改, 另一个线程可以看到最新的结果

    有序性

    一个线程内代码按照编写顺序执行, 没有指令重排的影响

    原子性

    一个线程内多行代码以一个整体运行, 期间不能有其他线程的代码插队. 没有线程切换的影响.

    示例代码验证

    volatile 不能保证原子性

    /**
     * volatile原子性例子: 不能保证原子性
     * 

    * 调试需要拆解"balance -= 5;" 使用Debug模式复现 * * @author xiaozhengN 571457082@qq.com * @since 2022-11-19 18:09:41 **/ @Slf4j public class AddAndSubtract { static volatile int balance = 10; private static void subtract() { balance -= 5; } private static void add() { balance += 5; } public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(2); new Thread(() -> { subtract(); latch.countDown(); }).start(); new Thread(() -> { add(); latch.countDown(); }).start(); latch.await(); log.info("共享变量balance: {}", balance); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    volatile 可保证可见性

    /**
     * volatile 可见性例子
     *
     * -Xint
     *
     * @author xiaozhengN 571457082@qq.com
     * @since 2022-11-19 17:36:20
     **/
    @Slf4j
    public class ForeverLoop {
    
        // 共享变量, 控制 foo() 循环次数
        static volatile boolean isStop = false;
    
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    log.error("", e);
                }
                isStop = true;
                log.info("修改共享变量成功: isStop: {}", isStop);
            }).start();
    
            new Thread(() -> {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    log.error("", e);
                }
                log.info("从主内存中获取共享变量, isStop: {}", isStop);
            }).start();
            foo();
        }
    
        static void foo() {
            int i = 0;
            while (!isStop) {
                i++;
            }
            log.info("foo方法已结束, 循环次数为: {}", i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    volatile设置内存屏障保证有序性(难理解)

    /**
     * volatile 有序性例子
     * 

    * 没有测试成功, 报错: Unrecognized VM option 'StressCCP' * java -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -jar jcstress.jar -t main.daily.Reordering.Case1 * D:\17-JDKS\jdk-16.0.1\bin\java -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -jar jcstress.jar -t main.daily.Reordering.Case1 * * @author xiaozhengN 571457082@qq.com * @since 2022-11-19 18:27:03 **/ public class Reordering { @JCStressTest @Outcome(id = {"0, 0", "1, 1", "0, 1"}, expect = Expect.ACCEPTABLE, desc = "可接受的值") @Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "感兴趣的值(发生指令重排)") @State public static class Case1 { int x; int y; @Actor public void actor1() { x = 1; y = 1; } @Actor public void actor2(II_Result result) { result.r1 = y; result.r2 = x; } } @JCStressTest @Outcome(id = {"0, 0", "1, 1", "0, 1"}, expect = Expect.ACCEPTABLE, desc = "可接受的值") @Outcome(id = "1, 0", expect = Expect.FORBIDDEN, desc = "被禁止的值") @State public static class Case2 { int x; volatile int y; @Actor public void actor1() { x = 1; y = 1; } @Actor public void actor2(II_Result result) { result.r1 = y; result.r2 = x; } } @JCStressTest @Outcome(id = {"0, 0", "1, 1", "0, 1"}, expect = Expect.ACCEPTABLE, desc = "可接受的值") @Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "感兴趣的值(发生指令重排)") @State public static class Case3 { volatile int x; int y; @Actor public void actor1() { x = 1; y = 1; } @Actor public void actor2(II_Result result) { result.r1 = y; result.r2 = x; } } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
  • 相关阅读:
    使用非空断言解决Typescript报错:对象可能为 “null“
    图像处理黑科技——弯曲矫正、去摩尔纹、切边增强、PS检测
    make/makefile
    Xilinx ISE系列教程(1):ISE开发环境下载、安装、注册(Windows 10 64位系统)
    参数估计的均方误差(MSE),偏置(Bias)与方差(Variance)分解,无偏估计
    某翻译网站webpack 全扣js逆向法
    kafka命令
    强化学习从基础到进阶-案例与实践[4.2]:深度Q网络DQN-Cart pole游戏展示
    Matlab|含多微网租赁共享储能的配电网博弈优化调度
    开源 DevOps 工具,你值得拥有!
  • 原文地址:https://blog.csdn.net/xiaozhengN/article/details/127947647