18.3.3 volatile关键字与内存可见性

1. volatile基础概念

1.1 volatile的作用和特性

public class VolatileBasics {
    
    // 普通变量 - 可能存在可见性问题
    private boolean normalFlag = false;
    
    // volatile变量 - 保证可见性
    private volatile boolean volatileFlag = false;
    
    public void demonstrateVisibility() {
        System.out.println("=== volatile可见性演示 ===");
        
        // 线程1:修改标志位
        Thread writer = new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("Writer: 设置volatile标志为true");
                volatileFlag = true;
                
                Thread.sleep(1000);
                System.out.println("Writer: 设置normal标志为true");
                normalFlag = true;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 线程2:读取标志位
        Thread reader = new Thread(() -> {
            while (!volatileFlag) {
                // 等待volatile标志变为true
            }
            System.out.println("Reader: 检测到volatile标志变化");
            
            if (normalFlag) {
                System.out.println("Reader: 也看到了normal标志变化");
            } else {
                System.out.println("Reader: 没有看到normal标志变化");
            }
        });
        
        writer.start();
        reader.start();
    }
}

1.2 volatile的三大特性

可见性

一个线程修改volatile变量,其他线程立即可见

解决缓存一致性问题

有序性

禁止指令重排序优化

保证代码执行顺序

原子性

对单个volatile变量的读写是原子的

64位变量的读写原子性

2. Java内存模型与可见性

2.1 JMM内存模型详解

public class JavaMemoryModel {
    
    private int sharedVariable = 0;
    private volatile int volatileVariable = 0;
    
    /*
     * Java内存模型(JMM):
     * 
     * 1. 主内存(Main Memory):所有变量的主存储区域
     * 2. 工作内存(Working Memory):每个线程的私有内存区域
     * 3. 内存交互操作:
     *    - read/load: 从主内存读取到工作内存
     *    - use/assign: 工作内存与执行引擎交互
     *    - store/write: 从工作内存写回主内存
     */
    
    public void demonstrateMemoryModel() {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // 写线程
        for (int i = 0; i < 2; i++) {
            final int threadId = i;
            executor.submit(() -> {
                for (int j = 0; j < 1000; j++) {
                    sharedVariable = threadId * 1000 + j;
                    volatileVariable = threadId * 1000 + j;
                    
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            });
        }
        
        executor.shutdown();
    }
}

3. volatile与指令重排序

3.1 指令重排序问题

public class InstructionReordering {
    
    private int a = 0;
    private boolean flag = false;
    private volatile boolean volatileFlag = false;
    
    /*
     * 指令重排序类型:
     * 1. 编译器重排序:编译器优化
     * 2. 指令级并行重排序:CPU指令级并行
     * 3. 内存系统重排序:缓存和读写缓冲区
     */
    
    public void demonstrateReordering() {
        for (int i = 0; i < 1000; i++) {
            a = 0;
            flag = false;
            volatileFlag = false;
            
            testNormalVariableReordering();
            testVolatileVariableReordering();
        }
    }
    
    private void testNormalVariableReordering() {
        Thread writer = new Thread(() -> {
            a = 1;          // 可能被重排序
            flag = true;    
        });
        
        Thread reader = new Thread(() -> {
            if (flag) {     
                if (a == 0) {   // 可能看到重排序结果
                    System.out.println("检测到普通变量重排序");
                }
            }
        });
        
        writer.start();
        reader.start();
    }
    
    private void testVolatileVariableReordering() {
        Thread writer = new Thread(() -> {
            a = 1;              // 不会被重排序到volatile写之后
            volatileFlag = true; // volatile写
        });
        
        Thread reader = new Thread(() -> {
            if (volatileFlag) { // volatile读
                // 由于happens-before规则,a=1必定可见
                assert a == 1;
            }
        });
        
        writer.start();
        reader.start();
    }
}

3.2 内存屏障机制

public class MemoryBarrier {
    
    private int x = 0;
    private volatile int volatileVar = 0;
    
    /*
     * 内存屏障类型:
     * 1. LoadLoad屏障:确保Load1先于Load2
     * 2. StoreStore屏障:确保Store1先于Store2
     * 3. LoadStore屏障:确保Load1先于Store2
     * 4. StoreLoad屏障:确保Store1先于Load2
     */
    
    

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务