You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何5线程环境下仅两个线程轮流调用synchronized方法?

问题原因分析

咱们先把两种写法的执行逻辑拆解开,就能明白为啥会出现只有两个线程轮流干活的情况了:

错误写法的核心问题:同步范围过大

不管是用synchronized方法,还是用synchronized块包裹整个while循环,本质都是让线程一旦拿到锁,就会在同步块内的循环里反复执行,直到调用wait()释放锁。具体流程是这样的:

  1. 初始5个线程同时抢锁,依次有线程5、1、2、3、4拿到锁,各执行一次售票逻辑,然后调用wait()释放锁,全部进入等待队列(Wait Set)。
  2. 最后执行的线程4调用notifyAll(),把所有等待队列里的线程都唤醒到锁的竞争队列(Entry Set),然后自己也进入等待队列。
  3. 这时候竞争队列里的线程开始抢锁,假设线程3抢到了——它会直接回到while循环的下一轮,执行售票、notifyAll()、sleep()、wait(),再次释放锁进入等待队列。
  4. 接下来锁释放时,线程4因为刚被唤醒,线程调度器大概率会优先调度它,导致线程3和4开始轮流抢锁、执行,剩下的线程1、2、5永远挤不进去,只能一直等。

简单说:这种写法下,抢到过锁的线程被唤醒后,会直接回到循环里继续抢锁,相当于“插队”了,其他线程根本没机会竞争。

正确写法的关键:每次循环重新竞争锁

把synchronized块放在while循环内部,相当于每次售票都要重新排队抢锁:

  1. 线程执行完wait()释放锁后,会退出同步块,回到while循环的条件判断。
  2. 当它再次尝试进入同步块时,会重新加入到锁的竞争队列尾部,和其他所有等待的线程公平竞争。
  3. 线程调度器这时候就能随机选择不同的线程,保证所有线程都有机会执行售票逻辑,也就出现了你想要的轮流执行效果。

额外提两个细节

  • Thread.sleep(10)不会释放锁,在同步块里执行会拉长线程持有锁的时间,但不是导致问题的核心原因。
  • System.exit(0)放在同步块内时,只要有一个线程执行到这行,整个程序直接终止;正确写法里每个线程都会先检查ticketsLeft,票卖完后所有线程都会退出循环,最终由某个线程触发退出。

内容的提问来源于stack exchange,提问作者Chingfung Yung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:38:52