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

Java多线程使用synchronized修饰方法为何未实现预期的线程交替执行效果

核心原因

首先可以确认的是,你代码里的synchronized完全生效了——你永远不会看到两个线程的输出行交叉混排,同一时刻确实只有一个线程在执行sendMessage(),互斥访问的目标已经达成。出现输出和预期不符的核心原因,是你对synchronized的能力存在预期偏差:

  • synchronized的核心作用仅为保证临界区的互斥访问,它从设计上根本不承担「锁公平分配」「线程执行顺序轮转」的职责,不会帮你把锁轮流分给两个等待的线程。
  • JVM对synchronized的锁实现本身就是偏向整体吞吐量的非公平策略:当一个线程刚释放对象锁时,调度器会优先让这个刚释放锁的线程再次尝试获取锁——因为此时该线程的CPU缓存处于热状态,重复拿锁执行的性能远高于唤醒另一个阻塞在锁等待队列里的线程。只要当前线程还没耗尽操作系统分配的CPU时间片,它完全可以连续多次抢到同一把锁,执行多轮同步方法后才会被强制挂起,此时等待的另一个线程才有机会拿到锁连续执行。
  • 你期待的「两个线程严格交替输出」属于有明确先后顺序的线程协同场景,本身就不是单靠加同步关键字能实现的效果:同步锁只解决“多个线程同时进临界区引发数据错乱”的互斥问题,不负责帮你编排两个线程“你一次我一次”的执行节奏。
连续同线程输出的形成逻辑

你的自定义线程类里是无任何阻塞的死循环结构:

@Override
public void run() {
    while (true) {
        messenger.sendMessage();
    }
}

整个运行流程完全由操作系统和JVM的调度逻辑决定:

  1. 线程拿到锁执行完打印、释放锁后,没有任何主动让出CPU的操作,会一直处于就绪状态等待调度
  2. 只要这个线程的CPU时间片没耗尽,调度器就会优先把资源分给它,它就能反复抢到锁,连续输出多轮日志
  3. 直到当前线程的时间片用完、被操作系统强制挂起,等待在锁队列里的另一个线程才会被唤醒拿到锁,开始连续输出自己的日志
    这就是你看到某一个线程连续打多行、突然切到另一个线程连续打多行的根本原因,代码本身没有语法或逻辑错误。
如何接近你预期的交替效果
  • 如果只是想避免单线程长时间占锁:可以在sendMessage()方法的逻辑末尾加Thread.yield()或者毫秒级的休眠,让当前线程主动让出CPU,给其他等待线程抢锁的机会。你可以简单测试:在死循环里加1ms的Thread.sleep(),就会看到原本连续十几行的同线程输出会被打散,出现接近交替的效果。
  • 如果需要严格的“你一次我一次”的交替输出:需要额外引入线程间通信机制,比如通过wait()/notify()维护共享状态标记,或者使用JUC包下的公平锁、条件队列做明确的执行顺序编排,不能仅依赖synchronized实现效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 12:57:05