synchronized能解决超卖问题,核心是将“检查库存+扣减库存”变为原子操作;锁对象必须唯一且覆盖整个临界区,静态变量用类锁或静态锁对象,实例变量推荐显式锁对象,并用while循环重检条件防止虚假唤醒。

synchronized 能解决超卖问题,核心是让“检查库存 + 扣减库存”变成一个不可分割的原子操作,确保同一时刻只有一个线程能执行这段逻辑。
锁对象必须唯一且作用于共享资源
多个线程操作的是同一个库存变量(比如 static int stock = 100),那它们就必须竞争同一把锁。常见错误是每个线程用自己的实例锁(如 this),结果形同虚设。
正确做法:
- 如果是静态变量,用
Ticket.class或专门的private static final Object lock = new Object() - 如果是实例变量且多线程共用同一个对象(如实现
Runnable),可用this;但更推荐显式锁对象,避免意外暴露
同步范围要精准覆盖读-改-写全过程
超卖的本质是“判断条件”和“修改动作”之间存在时间窗口。synchronized 必须把整个临界区包住,不能只锁扣减语句。
立即学习“Java免费学习笔记(深入)”;
错误示范(仍会超卖):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
if (stock > 0) {
synchronized(lock) { stock--; } // ❌ 判断在锁外,已失效
}
正确写法:
synchronized(lock) {
if (stock > 0) {
stock--;
System.out.println("卖出一张,剩余:" + stock);
} else {
System.out.println("售罄");
}
}
结合 wait/notify 实现库存不足时的等待唤醒
如果希望线程在库存为 0 时不直接退出,而是等待补货后再继续,可以用 wait() 和 notifyAll() 配合 synchronized。
注意点:
- 必须在 synchronized 块内调用,否则抛
IllegalMonitorStateException -
wait()会释放锁并挂起线程;notifyAll()唤醒所有等待线程重新竞争锁 - 被唤醒后需重新检查条件(用
while而非if),防止虚假唤醒
实际代码示例(Runnable + Class 锁)
这是最常用、最稳妥的写法:
public class TicketSeller implements Runnable {
private static int stock = 100;
private static final Object LOCK = new Object();
@Override
public void run() {
while (true) {
synchronized (LOCK) {
if (stock <= 0) {
System.out.println(Thread.currentThread().getName() + ":票已售完");
break;
}
try {
Thread.sleep(10); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
stock--;
System.out.println(Thread.currentThread().getName() + ":卖出1张,剩余" + stock);
}
}
}
public static void main(String[] args) {
TicketSeller seller = new TicketSeller();
new Thread(seller, "窗口1").start();
new Thread(seller, "窗口2").start();
new Thread(seller, "窗口3").start();
}
}
运行后,stock 最终为 0,不会出现负数,超卖被彻底拦截。

















