synchronized不适合读多写少场景,因其对所有访问强制串行化;优化策略是仅用它保护写操作,读操作不加锁并配合volatile保证可见性,同时缩小锁粒度、避免静态同步方法。

synchronized 本身并不适合读多写少场景,因为它对所有访问(无论读还是写)都强制串行化,会严重拖慢高并发读操作的吞吐量。直接用它处理读多写少,性能通常很差。但可以通过策略调整,让 synchronized 只承担“写”的同步职责,把“读”从锁中剥离出来,从而实现事实上的优化。
用 synchronized 保护写操作,读操作不加锁
核心思路是:读操作本身不修改共享状态,只要能保证可见性,就不必阻塞其他读线程。synchronized 天然具备可见性保障——线程释放锁时会刷新变量到主内存;其他线程获取同一把锁时会重新加载变量。因此,可只在写操作上加锁,读操作放开:
- 写方法(如 update、set、increment)用
synchronized或同步代码块包裹 - 读方法(如 get、size、toString)保持非同步,但确保被读字段是
volatile或由锁机制间接保证可见性 - 若读取的是复合状态(如多个字段需同时一致),则仍需整体加锁,此时已不属于纯“读多写少”优化范畴
配合 volatile 提升读性能与可见性
对于单个基本类型或引用类型的简单读写,推荐组合使用:volatile 保读的实时性 + synchronized 保写的原子性。例如计数器:
public class Counter {
private volatile long count = 0; // 读直接取,无需锁
<pre class="brush:php;toolbar:false;">public void increment() {
synchronized (this) {
count++; // 写必须原子,用锁保证
}
}
public long getCount() {
return count; // 读不加锁,volatile 保证看到最新值
}}
这样,1000 次读 + 1 次写,只有写那一次进锁,读完全无竞争。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
缩小锁粒度,避免读写互相阻塞
不要把读逻辑塞进同步块里。常见错误是:
public int getValue() {
synchronized (lock) {
log.debug("reading..."); // 日志、校验、格式转换等非临界操作
return value;
}
}
正确做法是只锁真正修改状态的最小片段:
- 把日志、计算、IO、对象构造等耗时或非共享操作移出同步块
- 如果读操作需要临时一致性快照(如复制数组),可在锁内完成拷贝,然后在锁外处理
- 对集合类,考虑用
CopyOnWriteArrayList替代手动加锁——它正是为读多写少设计的:读不加锁,写时复制底层数组
慎用静态同步方法,优先选择细粒度对象锁
静态同步方法锁的是 Class 对象,全局唯一,极易造成无关读写操作之间的意外阻塞。例如:
- 一个线程在调用
Config.load()(写配置) - 另一个线程在调用
Stats.getHitRate()(只读统计) - 若两者都是静态同步方法,就会相互等待,即使数据完全不相关
改用实例级锁或专用锁对象,可隔离不同业务维度的读写:
private final Object configLock = new Object();
private final Object statsLock = new Object();
<p>public void updateConfig() {
synchronized (configLock) { /<em> ... </em>/ }
}</p><p>public double getHitRate() {
synchronized (statsLock) { /<em> ... </em>/ }
// 读操作也可进一步放开,仅写时才锁
}</p>Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










