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

Java同步代码块释放锁后未被其他线程立即获取的原因咨询

为什么锁释放后当前线程先进入sell方法而非其他线程进入同步块

你的观察完全不是巧合,这是Java线程调度机制和代码执行流程共同作用的结果,拆解来看:

先理清楚代码的执行流程

  1. 初始启动阶段:三个线程共用同一个T2实例,启动后都会进入run方法的while循环,调用sell()方法——先打印enter sell method,然后进入10秒休眠。此时三个线程都处于休眠状态,没人抢锁。
  2. 第一次抢锁执行:10秒后三个线程陆续唤醒,开始竞争synchronized (this)的内置锁。假设Thread-2先抢到锁,进入同步块完成售票操作,随后释放锁。
  3. 锁释放后的执行顺序:Thread-2释放锁后,并不会立刻让出CPU——它当前还持有未耗尽的时间片,会继续执行sell()方法剩余的代码(同步块结束,方法返回),回到run方法的while循环(此时ticketNum=9≠0),再次调用sell()方法,于是先打印出Thread-2 enter sell method。而之前等待锁的Thread-1此时才从阻塞状态进入就绪队列,等待调度获取锁,之后才会打印enter sync code block。

核心机制:线程调度的优先级规则

Java线程调度器采用时间片轮转(默认优先级相同的情况下),当一个线程释放锁后:

  • 它本身处于运行状态,且时间片未耗尽,调度器会优先让它继续执行后续代码,而不是立刻切换到其他线程。
  • 等待锁的线程之前处于阻塞状态,锁释放后需要先进入就绪队列排队等待调度,这个过程存在调度延迟,自然慢于当前运行的线程。

额外的代码线程安全问题

顺便提一下你代码里的隐患:

  • run方法里的while (ticketNum != 0)判断不是线程安全的:ticketNum没有被volatile修饰,其他线程对它的修改可能无法及时被当前线程感知,导致线程多执行几次sell()。
  • 即使加了volatile,while判断和sell()调用之间也存在竞态条件,可能出现多个线程同时进入sell()方法的情况。

建议把判断和售票逻辑都放到同步块里,比如修改后的run方法:

@Override
public void run() {
    while (true) {
        synchronized (this) {
            if (ticketNum <= 0) {
                break;
            }
            try {
                Thread.sleep(5000);
                ticketNum -= 1;
                System.out.println(Thread.currentThread().getName() + " sell one ticket, rest " + ticketNum + "\n");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:35:29