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

目的

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

资源

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

如何理解线程安全

参考: 什么是线程安全

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

可见性

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

有序性

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

原子性

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

示例代码验证

volatile 不能保证原子性

/**
 * volatile原子性例子: 不能保证原子性
 * <p>
 * 调试需要拆解"balance -= 5;" 使用Debug模式复现
 *
 * @author xiaozhengN [email protected]
 * @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);
    }
}

volatile 可保证可见性

/**
 * volatile 可见性例子
 *
 * -Xint
 *
 * @author xiaozhengN [email protected]
 * @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);
    }
}

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

/**
 * volatile 有序性例子
 * <p>
 * 没有测试成功, 报错: 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 [email protected]
 * @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;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/xiaozhengN/article/details/127947647