synchronized 不适合高并发,关键在规避竞争:缩小锁范围、按业务维度分段加锁、用 threadlocal 避免共享。
单纯靠 synchronized 实现“高并发锁”本身是个有误导性的说法——synchronized 是悲观锁、阻塞式、串行化机制,天然不擅长“高并发”场景。它保障的是绝对安全,而非高吞吐或低延迟。但如果你的约束很明确:不引入 java.util.concurrent(juc)包,只用原生 synchronized + jvm 基础能力,仍希望在真实业务中支撑较高并发量,关键不在“造新锁”,而在规避锁竞争、缩小锁范围、减少临界区执行时间。
精准控制锁对象,避免全局/类级大锁
很多性能问题源于锁粒度太粗。例如:
- 错:用
synchronized (this)或synchronized (MyClass.class)锁住整个实例或整个类——所有线程排队等同一把锁; - 对:为每个业务维度(如用户ID、订单号、设备号)创建独立锁对象,实现逻辑上的“行锁”效果。
示例:按用户ID分段加锁
// 预先初始化固定大小的锁数组(避免动态new太多对象)
private final Object[] userLocks = new Object[1024];
{
for (int i = 0; i
<p>这样就把原本可能上万用户争抢一把锁,变成最多几十个用户争抢一把锁,显著降低竞争概率。</p>
<h3>用双重检查 + volatile 避免重复初始化开销</h3>
<p>当同步块内是“首次创建资源”的逻辑(如单例、缓存加载),不要每次进都加锁。用 volatile + double-checked locking 模式把锁只留在真正需要初始化的那一刻:</p>
<pre class="brush:java;toolbar:false;">private volatile ExpensiveResource resource;
public ExpensiveResource getResource() {
if (resource == null) { // 第一次检查(无锁)
synchronized (this) {
if (resource == null) { // 第二次检查(有锁,防重复创建)
resource = new ExpensiveResource();
}
}
}
return resource;
}
注意:volatile 是必须的,防止指令重排序导致其他线程看到未构造完成的对象。
用 wait/notify 做条件等待,避免空转轮询
如果业务需要“等待某个状态就绪”(如库存扣减前等补货完成),别用 while + sleep 轮询,而是配合 synchronized 块内的 wait() 和 notifyAll():
private final Object stockLock = new Object();
private int stock = 100;
public boolean tryDeduct(int amount) {
synchronized (stockLock) {
while (stock
<p>这比 busy-wait 节省 CPU,也比无差别加锁更高效。</p>
<h3>结合不可变对象与线程封闭,从源头消灭锁需求</h3>
<p>最高效的“不加锁”,是根本不需要锁:</p>
- 返回值尽量用
String、Integer、LocalDateTime等不可变对象; - 方法内创建的集合、DTO、Builder 实例,只在当前方法栈使用(栈封闭),不逃逸到共享堆内存;
- 用
ThreadLocal维护线程独享上下文(如数据库连接、用户会话、traceId),无需同步。
例如日志上下文:
private static final ThreadLocal<string> traceIdHolder = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public String getCurrentTraceId() {
return traceIdHolder.get();
}</string>
每个线程读写自己的副本,零竞争、零同步、零开销。
不依赖 JUC 也能应对不少中高并发场景,核心不是让 synchronized 更快,而是让它尽量少出现、尽量轻量、尽量不挡路。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











