volatile关键字的作用?
1 个回答
自己喜欢的东西,就不要问别人好不好。赵云#### **`volatile` 关键字的作用** `volatile` 关键字在 Java 中用于修饰共享变量(类的成员变量、类的静态成员变量)。一旦变量被 `volatile` 修饰,它具备以下两层语义: ### **1. 保证可见性** - **解释**: 当一个共享变量被 `volatile` 修饰时,任何线程对该变量的修改都会立即写回主存。而其他线程在读取该变量时,都会从主存中获取最新值,而非从自己的线程缓存(工作内存)中读取旧值。 - **实现**: - 使用缓存一致性协议(如 MESI 协议),在一个线程修改 `volatile` 变量时,会将该变量在其他线程的缓存中标记为无效。 - 其他线程在读取变量时发现缓存无效,强制从主存中加载最新值。 - **注意**:`volatile` 并不会禁用 CPU 缓存,而是通过一致性协议管理缓存数据的更新。 - **重要点**: - 如果缓存未被标记为无效,则线程仍会从缓存中读取值,`volatile` 的可见性依赖于缓存一致性协议的正确工作。 ### **2. 禁止指令重排序** - **解释**: - `volatile` 的读/写操作前后会插入内存屏障(Memory Barrier),确保变量的操作不会因编译器优化或 CPU 重排序而导致执行顺序混乱。 - 这对于保证多线程操作的正确性非常重要。 ### **`volatile` 和高速缓存的关系** `volatile` 的工作机制并不是禁用高速缓存,而是确保通过缓存一致性协议来保持变量的可见性。具体来说: 1. **修改变量时**: - 当线程修改 `volatile` 变量后,会强制将新值写回主存,并通过缓存一致性协议通知其他 CPU,将它们缓存中存储的该变量的值标记为无效。 2. **读取变量时**: - 如果缓存被标记为无效,则线程会从主存加载最新值。 - 如果缓存仍有效,则线程可以直接从缓存中读取,避免频繁访问主存。 ### **`volatile` 和 `synchronized` 的对比** #### **1. 可见性** - `volatile`:保证可见性。 - `synchronized`:同时保证可见性和排他性(独占访问)。 #### **2. 原子性** - `volatile`:不能保证操作的原子性(如 `i++` 不是原子操作)。 - `synchronized`:可以保证原子性。 #### **3. 使用场景** - `volatile`:适用于状态标志、配置参数等简单场景,特别是需要可见性但不需要排他性或原子性的情况。 - `synchronized`:适用于需要独占访问的复杂操作场景,特别是涉及多步骤操作或临界区保护。 ### 修正后的代码示例 以下代码演示了 `volatile` 的作用和局限性: #### **1. 可见性示例** ```java public class VolatileVisibility { private volatile boolean flag = true; public void writer() { flag = false; // 修改后,立即对其他线程可见 } public void reader() { while (flag) { // 一直读取最新值 } System.out.println("Flag has been updated to false"); } public static void main(String[] args) { VolatileVisibility example = new VolatileVisibility(); Thread writerThread = new Thread(example::writer); Thread readerThread = new Thread(example::reader); readerThread.start(); writerThread.start(); } } ``` #### **2. 原子性问题示例** ```java public class VolatileAtomicity { private volatile int count = 0; public void increment() { count++; // 不是原子操作,存在线程安全问题 } public static void main(String[] args) { VolatileAtomicity example = new VolatileAtomicity(); for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { example.increment(); } }).start(); } try { Thread.sleep(2000); // 等待所有线程执行完毕 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final count: " + example.count); // 输出可能小于10000 } } ``` ### **总结** - `volatile` 保证了变量的 **可见性** 和 **禁止指令重排序**,但不能保证原子性。 - 它的可见性通过 **缓存一致性协议** 实现,而不是完全禁用缓存。 - 在多线程场景中,`volatile` 和 `synchronized` 适用于不同的场景,需要根据需求选择合适的工具。编辑于 2024-10-12 15:26 •IP属地 美国举报评论收藏
公众号进入网站体验更佳

