【问题标题】:How synchronized is working in the following threading code and what is the code flow?以下线程代码中的同步工作情况如何?代码流程是什么?
【发布时间】:2025-11-30 09:10:01
【问题描述】:
class ThreadA {
public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    synchronized (b) {
        try {
            System.out.println("Waiting for b to complete...");
            b.wait();
        } catch (InterruptedException e) {
        }
        System.out.println("Total is: " + b.total);
    }
}}

class ThreadB extends Thread {
int total;

public void run() {
    synchronized (this) {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
        notify();
    }
}}

我无法理解上述程序中的代码流程以及如何调用 run 方法。 当对象属于ThreadB 类时,为什么ThreadAmain() 线程能够同步对象b。除此之外,当main() 线程遇到wait() 时,执行如何转移到ThreadB 中的run()

【问题讨论】:

    标签: java multithreading synchronization runnable


    【解决方案1】:

    为什么 main() 甚至在 ThreadB 的 run() 能够执行之前到达同步块。

    你的程序有一个竞态条件

    main() 调用b.start() 之后,有两个线程正在运行,未同步,它们竞相锁定(即同步)b 对象。无法预测哪个线程会先锁定它。

    如果main()线程先锁定它,那么它会调用b.wait()。这将暂时解锁b 对象,并允许其他线程运行。另一个线程将完成它的工作,调用b.notify() 并退出synchronized 块。一旦它退出,main() 线程将能够从wait() 调用中返回。

    如果第二个线程首先锁定b 对象,则行为会有所不同:它将完成它的工作,调用notify(),然后返回。但在这种情况下,notify() 调用将不会执行任何操作,因为在尝试进入synchronized 块时,main() 线程将被阻塞。它不会在 b.wait() 调用中等待,如果没有任何其他线程在等待,notify() 不会做任何事情。

    那么在这种情况下接下来会发生什么,当ThreadB 线程离开synchronized 块时,main() 线程将能够进入它并调用wait()。程序将在此时挂起,因为ThreadB 线程已经完成工作并死亡。没有其他线程可以调用notify()

    当两个(或更多)线程竞相做某事时,我们称之为竞态条件,程序的输出取决于哪个线程先到达那里。

    【讨论】:

    • 您对竞争条件的看法是对的,但是每次我执行程序时 main() 是如何作为第一个线程执行的。我知道其他线程不需要执行,但我已经尝试了至少 100 次并且没有得到任何不同的结果。
    • @NikhilArora,Java 语言规范 (JLS)允许您的程序为您提供您所看到的结果。它还允许您的程序以我描述的其他方式工作。它的实际行为方式可能取决于很多事情。在您正在运行的 JVM 版本上,在操作系统版本上,在处理器架构上,在机器上运行的其他进程上,......这就是为什么测试不是在多线程程序中发现问题的可靠方法:你无法控制的变数太多了。
    【解决方案2】:

    除了 main() 线程遇到 wait() 时,执行如何转移到 ThreadB 中的 run()。

    调用wait()之前

    如果下一步会调用wait method,则表示主线程当前持有对象b监视器。如果 threadB 想要执行 run() 方法,它将被阻塞等待 main thread 持有的 bmonitor

    调用 wait()

    表示main thread释放monitor,进入等待状态,通知thread scheduler re_dispatch。然后ThreadB就可以得到monitor,做事了。

    通知()

    完成它的工作后,threadB 调用 notify() 来唤醒一个正在该对象的 monitor 上等待的单个线程,这里是 主线程时间>。 主线程会继续工作。

    【讨论】:

    • 为什么 main() 甚至在 ThreadB 的 run() 能够执行之前到达同步块。 main() 如何在 b(ThreadB 类的对象)上执行同步块。
    • @Nikhil Arora 因为main thread可以得到b的monitor,那么它就可以执行同步块,与ThreadB的run()是否能够执行无关执行。而 b 上的同步块是由 thread b 通过执行 b.start() 而不是由 main thread 执行的。
    最近更新 更多