当前位置: 首页 > news >正文

volatile vs synchronized:Java 并发两大护法

volatile vs synchronized:Java 并发两大"护法"

volatilesynchronized是 Java 解决并发问题的两个核心关键字,但它们的原理、适用场景和成本完全不同。

核心区别总览

特性volatilesynchronized
本质轻量级,变量可见性保证重量级,原子性+可见性保证
作用范围只能修饰变量可以修饰方法、代码块
原子性❌ 不保证(对复合操作无效)✅ 保证
可见性✅ 保证(立即刷新到主内存)✅ 保证
有序性✅ 禁止指令重排序✅ 保证有序性
阻塞❌ 不会阻塞线程✅ 会阻塞线程(锁)
性能高(几乎无开销)低(上下文切换开销)
使用场景状态标志、双重检查锁临界区、资源争用

volatile 详解

1. 可见性保证

没有 volatile 的问题

public class VisibilityProblem { private static boolean flag = false; // ❌ 没有volatile public static void main(String[] args) throws InterruptedException { Thread writer = new Thread(() -> { try { Thread.sleep(1000); // 模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } flag = true; // 修改标志 System.out.println("Writer: flag 设置为 true"); }); Thread reader = new Thread(() -> { while (!flag) { // 可能永远循环!因为看不到flag的变化 } System.out.println("Reader: 检测到 flag 为 true"); }); writer.start(); reader.start(); writer.join(); reader.join(); } } // 输出可能只有:Writer: flag 设置为 true // Reader线程可能永远看不到变化

使用 volatile 解决

public class VisibilitySolution { private static volatile boolean flag = false; // ✅ 加上volatile public static void main(String[] args) throws InterruptedException { Thread writer = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; // 修改会立即刷到主内存 System.out.println("Writer: flag 设置为 true"); }); Thread reader = new Thread(() -> { while (!flag) { // 现在能看到变化了! } System.out.println("Reader: 检测到 flag 为 true"); }); writer.start(); reader.start(); writer.join(); reader.join(); } } // 正确输出: // Writer: flag 设置为 true // Reader: 检测到 flag 为 true

2. 禁止指令重排序

没有 volatile 的重排序问题

public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 // ❌ 问题:这里可能重排序! instance = new Singleton(); // 实际执行可能: // 1. 分配内存 // 2. 引用赋值(instance不为null了) // 3. 初始化对象 // 其他线程可能拿到未初始化的对象! } } } return instance; } }

使用 volatile 解决

public class Singleton { private static volatile Singleton instance; // ✅ 加上volatile private Singleton() { // 初始化操作 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); // ✅ 不会重排序 } } } return instance; } }

3. volatile 的局限性

public class VolatileLimitation { private volatile int count = 0; public void increment() { count++; // ❌ 这不是原子操作! // 实际是3步: // 1. 读取count // 2. count + 1 // 3. 写回count // 多线程会丢失更新 } public static void main(String[] args) throws InterruptedException { VolatileLimitation demo = new VolatileLimitation(); Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { demo.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { demo.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("最终结果: " + demo.count); // 可能输出:15678(而不是20000) } }

synchronized 详解

1. 基本用法

public class SynchronizedDemo { private int count = 0; private final Object lock = new Object(); // 1. 实例方法同步 public synchronized void increment1() { count++; } // 2. 静态方法同步 public static synchronized void staticMethod() { // 锁是 SynchronizedDemo.class } // 3. 同步代码块(指定锁对象) public void increment2() { synchronized (lock) { // 使用特定对象作为锁 count++; } } // 4. 同步代码块(this锁) public void increment3() { synchronized (this) { // 使用当前实例作为锁 count++; } } }

2. 保证原子性

public class AtomicityDemo { private int count = 0; public synchronized void safeIncrement() { count++; // ✅ 原子操作 } public void unsafeIncrement() { count++; // ❌ 非原子操作 } public static void main(String[] args) throws InterruptedException { AtomicityDemo demo = new AtomicityDemo(); // 测试安全版本 Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { demo.safeIncrement(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { demo.safeIncrement(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("安全版本结果: " + demo.count); // 总是20000 } }

3. 锁的升级过程

// JDK 1.6后的synchronized优化 public class LockUpgrade { private Object obj = new Object(); public void method() { synchronized (obj) { // 锁状态变化: // 1. 无锁 → 偏向锁(单线程) // 2. 偏向锁 → 轻量级锁(有竞争但不多) // 3. 轻量级锁 → 重量级锁(竞争激烈) // 锁消除、锁粗化等优化 } } }

实际场景对比

场景1:状态标志

// ✅ 适合用 volatile public class ShutdownHook { private volatile boolean shutdownRequested = false; public void shutdown() { shutdownRequested = true; // 立即对所有线程可见 } public void doWork() { while (!shutdownRequested) { // 执行工作 } System.out.println("接收到关闭信号,优雅退出"); } } // ❌ 不适合用 synchronized(太重) public class ShutdownHookHeavy { private boolean shutdownRequested = false; public synchronized void shutdown() { shutdownRequested = true; } public void doWork() { while (true) { synchronized (this) { if (shutdownRequested) { break; } } // 执行工作 } System.out.println("接收到关闭信号,优雅退出"); } }

场景2:计数器

// ❌ 不适合用 volatile public class Counter { private volatile int count = 0; public void increment() { count++; // 仍然线程不安全! } public int getCount() { return count; } } // ✅ 适合用 synchronized public class SafeCounter { private int count = 0; public synchronized void increment() { count++; // 线程安全 } public synchronized int getCount() { return count; } } // ✅ 更好的方案:AtomicInteger import java.util.concurrent.atomic.AtomicInteger; public class BetterCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); // CAS操作,无锁 } public int getCount() { return count.get(); } }

场景3:单例模式

// 方案1:synchronized 方法(简单但性能差) class Singleton1 { private static Singleton1 instance; private Singleton1() {} public static synchronized Singleton1 getInstance() { if (instance == null) { instance = new Singleton1(); } return instance; } } // 方案2:双重检查锁 + volatile class Singleton2 { private static volatile Singleton2 instance; private Singleton2() {} public static Singleton2 getInstance() { if (instance == null) { synchronized (Singleton2.class) { if (instance == null) { instance = new Singleton2(); } } } return instance; } } // 方案3:静态内部类(推荐) class Singleton3 { private Singleton3() {} private static class Holder { static final Singleton3 INSTANCE = new Singleton3(); } public static Singleton3 getInstance() { return Holder.INSTANCE; } }

性能对比测试

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class PerformanceComparison { private static final int THREAD_COUNT = 100; private static final int LOOP_COUNT = 100000; private volatile int volatileCount = 0; private int synchronizedCount = 0; private AtomicInteger atomicCount = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { PerformanceComparison demo = new PerformanceComparison(); System.out.println("=== volatile 测试 ==="); long time1 = demo.testVolatile(); System.out.println("volatile 最终值: " + demo.volatileCount); System.out.println("\n=== synchronized 测试 ==="); long time2 = demo.testSynchronized(); System.out.println("synchronized 最终值: " + demo.synchronizedCount); System.out.println("\n=== AtomicInteger 测试 ==="); long time3 = demo.testAtomic(); System.out.println("AtomicInteger 最终值: " + demo.atomicCount.get()); System.out.println("\n=== 性能对比 ==="); System.out.println("volatile 耗时: " + time1 + "ms"); System.out.println("synchronized 耗时: " + time2 + "ms"); System.out.println("AtomicInteger 耗时: " + time3 + "ms"); } private long testVolatile() throws InterruptedException { volatileCount = 0; CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long start = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(() -> { for (int j = 0; j < LOOP_COUNT; j++) { volatileCount++; // ❌ 不安全,但测性能 } latch.countDown(); }).start(); } latch.await(); return System.currentTimeMillis() - start; } private long testSynchronized() throws InterruptedException { synchronizedCount = 0; CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long start = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(() -> { for (int j = 0; j < LOOP_COUNT; j++) { incrementSync(); } latch.countDown(); }).start(); } latch.await(); return System.currentTimeMillis() - start; } private synchronized void incrementSync() { synchronizedCount++; } private long testAtomic() throws InterruptedException { atomicCount.set(0); CountDownLatch latch = new CountDownLatch(THREAD_COUNT); long start = System.currentTimeMillis(); for (int i = 0; i < THREAD_COUNT; i++) { new Thread(() -> { for (int j = 0; j < LOOP_COUNT; j++) { atomicCount.incrementAndGet(); } latch.countDown(); }).start(); } latch.await(); return System.currentTimeMillis() - start; } }

内存模型与 happens-before

volatile 的 happens-before

public class HappensBefore { private int x = 0; private volatile boolean v = false; public void writer() { x = 42; // 1 v = true; // 2 } public void reader() { if (v) { // 3 // 这里一定能看到 x == 42 // 因为 1 happens-before 2 // 2 happens-before 3(volatile写-读规则) // 所以 1 happens-before 3 System.out.println(x); // 输出42 } } }

synchronized 的 happens-before

public class SynchronizedHappensBefore { private int x = 0; public synchronized void writer() { x = 42; // 1 } // 2 解锁 public synchronized void reader() { // 3 加锁 // 这里一定能看到 x == 42 // 因为 1 happens-before 2(程序顺序) // 2 happens-before 3(监视器锁规则) System.out.println(x); // 输出42 } // 4 解锁 }

常见错误

错误1:误用 volatile

// ❌ 错误:以为 volatile 能保证原子性 public class WrongVolatile { private volatile int a = 0; private volatile int b = 0; public void set() { a = 1; // 操作1 b = 2; // 操作2 // 其他线程可能看到 b=2 但 a!=1 // volatile 不保证多个变量的原子性 } }

错误2:过度同步

// ❌ 错误:不需要同步的地方用了同步 public class OverSynchronized { private final List<String> list = Collections.synchronizedList(new ArrayList<>()); public void addIfAbsent(String item) { synchronized (list) { // ❌ 多余的同步 if (!list.contains(item)) { list.add(item); } } // Collections.synchronizedList 已经同步了 } }

错误3:锁对象不一致

// ❌ 错误:使用不同的锁对象 public class WrongLock { private final Object lock1 = new Object(); private final Object lock2 = new Object(); private int count = 0; public void increment() { synchronized (lock1) { // 用lock1 count++; } } public int get() { synchronized (lock2) { // ❌ 用lock2,无法保护count return count; } } }

最佳实践

1. 选择原则

// 如何选择 volatile 还是 synchronized? if (满足以下所有条件) { return "用 volatile"; // 1. 对变量的写操作不依赖当前值 // 2. 该变量没有包含在具有其他变量的不变式中 // 3. 访问变量时不需要加锁 } else { return "用 synchronized 或其他锁机制"; }

2. 现代替代方案

// 1. 用 Atomic 类代替 volatile private volatile int count; // 旧方式 private AtomicInteger count = new AtomicInteger(); // 新方式 // 2. 用 ReentrantLock 代替 synchronized private final ReentrantLock lock = new ReentrantLock(); public void method() { lock.lock(); try { // 临界区 } finally { lock.unlock(); } } // 3. 用 StampedLock 优化读多写少 private final StampedLock lock = new StampedLock(); public void read() { long stamp = lock.tryOptimisticRead(); // 读操作... if (!lock.validate(stamp)) { stamp = lock.readLock(); try { // 重新读 } finally { lock.unlockRead(stamp); } } }

3. 性能优化技巧

// 1. 缩小同步范围 public void slowMethod() { // 很多不需要同步的代码... synchronized (this) { // 只有这一小段需要同步 } // 更多不需要同步的代码... } // 2. 使用读写锁分离 public class ReadWriteCache { private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private Map<String, Object> cache = new HashMap<>(); public Object get(String key) { lock.readLock().lock(); try { return cache.get(key); } finally { lock.readLock().unlock(); } } public void put(String key, Object value) { lock.writeLock().lock(); try { cache.put(key, value); } finally { lock.writeLock().unlock(); } } }

总结

volatile 是"轻量级同步"

  • 只保证可见性禁止重排序
  • 不保证原子性
  • 适合状态标志双重检查锁

synchronized 是"重量级同步"

  • 保证原子性可见性有序性
  • 阻塞线程
  • 适合临界区保护

选择指南

  1. 如果只是一个线程写,多个线程读的状态标志 →volatile
  2. 如果需要复合操作的原子性synchronized
  3. 如果竞争不激烈→ 考虑synchronized优化后的轻量级锁
  4. 如果读多写少→ 考虑ReadWriteLock
  5. 如果只是计数器→ 用Atomic

记住核心volatile解决可见性问题,synchronized解决原子性问题。理解它们的差异,才能写出正确高效的多线程代码。

http://www.jsqmd.com/news/551069/

相关文章:

  • 如何解决视频抠像中的稳定性难题:MatAnyone一致性内存传播技术详解
  • 3分钟让静态图像“活“起来:AI肖像动画技术全解析
  • 从‘我有个秘密’到‘我签个名’:用Python代码一步步还原Schnorr协议的核心流程
  • 游戏存档备份终极指南:如何用Ludusavi守护你的珍贵游戏进度
  • MySQL 8.0 等保合规实战:手把手配置开源审计插件 server_audit.so
  • 重新定义音频体验:Equalizer APO 5大技术突破与实战指南
  • Diagnostic Log and Trace——DLT 日志级别与上下文管理的最佳实践
  • 量子力学的抽象地位与c语言等价
  • RTX4090D显存监控:OpenClaw长期运行Qwen3-32B-Chat的资源分析
  • 农业图像标注效率暴跌63%?这5个Auto-Labeling技巧已获农业农村部AI应用白皮书推荐
  • DAMOYOLO-S跨域适应能力展示:在动漫风格图像上的目标检测
  • 2026年3月二手平地机厂家推荐,二手徐工平地机、二手卡特平地机、180平地机出口实力源头厂商 - 品牌企业推荐师(官方)
  • HunyuanVideo-Foley惊艳作品集:10类生活场景AI生成Foley音效展示
  • 告别重复劳动:8个AI驱动的Illustrator脚本让设计效率提升10倍
  • 从原理到产业:一文读懂Synthesizer V音频生成技术
  • 括号生成-leetcode
  • 在Blender中玩转Stable Diffusion:AI-Render让3D设计进入智能创作时代
  • QEMU模拟环境下uboot引导Linux内核的完整流程解析
  • P3387 【模板】缩点
  • 告别Conda激活玄学:从`conda init`原理到一键修复脚本(Win/Mac/Linux通用)
  • 提升arduino开发效率:用快马平台一键生成常用工具模块代码
  • VisualCppRedist AIO:一站式解决Windows系统运行库依赖难题
  • Stable-Diffusion-V1-5 保姆级部署:Node.js环境配置与自动化脚本编写
  • 零基础也能挖洞赚钱?SRC漏洞挖掘从入门到精通,附全套工具包+学习路线!
  • 口碑好的企业号码认证服务商有哪些?实现手机座机显示公司名 - 企业服务推荐
  • 告别top和netstat:用sysdig一个命令搞定Linux系统监控(含Docker容器)
  • OpenClaw安全防护指南:Qwen3-32B操作权限管控与敏感数据隔离
  • md2pptx:如何通过Markdown实现演示文稿的高效制作与自动化管理
  • STM32CubeMX配置Qwen-Image-Edit-F2P物联网终端应用
  • 避开FPGA时序分析盲区:除了Clock和Data,别忘了用Set_Data_Check给你的控制信号也上个‘闹钟’