You need to enable JavaScript to run this app. Java使用继承Thread类与ReentrantLock售票为何出现0号票?
优惠活动
大模型
产品
解决方案
定价
更多

Java使用继承Thread类与ReentrantLock售票为何出现0号票?

异常成因分析
  • 核心根因是锁对象未被多线程共享,同步逻辑完全失效
    代码中ReentrantLock是ThreadOne类的非静态实例属性,每创建一个ThreadOne对象就会生成一个独立的锁实例。main方法中分别实例化了t1、t2两个ThreadOne对象作为两个售票窗口,两个窗口持有的是完全不同的锁对象,不存在互斥关系——这是使用显式锁时非常常见的误区:同步生效的核心前提是多线程争抢同一个锁实例,仅调用lock方法本身不会产生互斥效果,相当于两个窗口各自带了独立的锁,完全不会拦截对方进入售票逻辑,自然会出现竞态条件。
  • 触发售卖0号票的具体时序过程:

    当余票仅剩1张时,两个线程因为持有的锁独立,可以同时进入售票判断分支:

    1. 两个线程同时读取到共享的ticket=1,判定满足ticket>0的售票条件
    2. 两个线程先后执行sleep模拟售票耗时,Window 2 先醒,打印"卖出1号票",并将ticket值更新为0
    3. Window 1 随后睡醒,没有重新判断余票状态,直接读取当前已经被改成0的ticket值打印,随后将ticket减为-1,最终出现售卖0号票的异常输出。
对应解决方法
  • 调整锁的作用域,让锁成为所有线程共享的类级别静态属性,和同样是静态共享的ticket属性保持一致的作用粒度:
    将原代码中定义的实例锁:
    private ReentrantLock lock = new ReentrantLock();
    
    修改为静态共享锁,同时加final修饰避免锁实例被意外修改:
    private static final ReentrantLock lock = new ReentrantLock();
    
  • 调整后的逻辑验证:所有售票线程会争抢同一个锁对象,同一时刻只会有一个线程进入临界区执行售票逻辑。当某一个线程持锁进入临界区时,会完整执行「判断余票→打印售票信息→扣减余票」的全流程,执行完再释放锁;其他线程拿到锁后会重新判断余票值,当余票为0时直接跳出循环,不会进入打印流程,从根源上避免售卖0号票的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.06 18:45:01