synchronized是Java最基础的线程同步机制,通过对象锁实现互斥访问,保证原子性、可见性和有序性;它有三种用法:实例方法锁this、静态方法锁Class、同步代码块锁指定对象,推荐细粒度加锁并避免锁内耗时操作。

synchronized 是 Java 最基础、最常用的线程同步机制,它通过“对象锁”控制多线程对共享资源的互斥访问,天然解决竞态条件、数据不一致等并发安全问题。用得好,代码简洁可靠;用得不当,容易引发性能瓶颈甚至死锁。
三种典型加锁方式与对应锁对象
理解锁对象是正确使用 synchronized 的前提,不同写法锁的目标完全不同:
- 实例方法加锁:锁的是当前对象(this)。多个线程调用同一个对象的该方法时会串行执行;但调用不同对象的同一方法,彼此不干扰。
-
静态方法加锁:锁的是当前类的 Class 对象(如
Counter.class)。所有实例共用一把锁,适合保护类级别的共享状态(如计数器总数、配置缓存)。 -
同步代码块加锁:显式指定锁对象,例如
synchronized(lockObj) { ... }。推荐使用私有 final 对象(如private final Object lock = new Object();),避免外部误操作导致锁失效或意外争用。
为什么必须关注锁粒度和临界区范围
锁不是越“大”越安全,而是越“准”越高效。粗粒度锁(如整个方法)易导致线程排队等待,吞吐下降;细粒度锁(精准包裹共享变量读写)可提升并发度,但需确保逻辑完整性。
- 不要把耗时操作(如 I/O、远程调用、复杂计算)放在 synchronized 块内,否则其他线程会长时间阻塞。
- 避免在同步块中调用外部可能持有其他锁的方法,尤其不能嵌套调用另一个 synchronized 方法——极易引发死锁。
- 如果只是读取共享变量且后续无依赖写操作,通常无需同步(但要注意可见性,此时 volatile 更轻量)。
常见并发问题与 synchronized 的应对逻辑
synchronized 不仅能保证原子性,还自带内存可见性和有序性保障,这是它比手动加锁更安全的关键:
立即学习“Java免费学习笔记(深入)”;
-
竞态条件:如
count++实际包含读-改-写三步,多个线程并发执行会导致丢失更新。synchronized 将其封装为不可分割的临界区,确保每次完整执行。 - 变量不可见:线程 A 修改了共享变量后未及时刷回主内存,线程 B 仍读到旧值。synchronized 在释放锁前强制刷新所有修改,在获取锁时强制从主内存重读变量。
- 指令重排序干扰:JVM 或 CPU 可能优化指令顺序,破坏多线程下的逻辑依赖。synchronized 建立 happens-before 关系,禁止临界区内外的跨边界重排。
对比 ReentrantLock 的实用选择建议
synchronized 简单可靠,适合大多数场景;ReentrantLock 更灵活,但需手动管理锁的获取与释放:
- 优先用 synchronized:代码简洁、不易出错、JVM 有深度优化(偏向锁、轻量级锁等),日常开发中 80% 以上同步需求它都能胜任。
- 考虑 ReentrantLock 的情况:需要尝试获取锁(
tryLock())、设定超时、实现公平锁、或配合 Condition 实现精确唤醒(如生产者-消费者模型)。 - 注意:synchronized 不可中断等待、不支持条件变量,也不像 ReentrantLock 那样能查询锁状态或获取等待队列信息。


















