【问题标题】:Why is run() not immediately called when start() called on a thread object in javajava - 为什么在java中的线程对象上调用start()时没有立即调用run()
【发布时间】:2010-04-22 11:10:25
【问题描述】:

是吗?
我有一个线程对象来自:

Thread myThread = new Thread(pObject);

其中 pObject 是实现 Runnable 接口的类的对象,然后我在线程对象上调用 start 方法,如下所示:

myThread.start();

现在,我的理解是,当 start() 被调用时,JVM 会隐式(并立即)调用可能被覆盖的 run() 方法(就像我的情况一样)

但是,在我的情况下,似乎 start() 方法没有立即调用(根据需要),而是直到其他语句/方法从调用块完成,即如果我在 start() 调用之后有一个方法像这样:

myThread.start();
doSomethingElse();

doSomthingElse() 在 run() 方法运行之前被执行。
也许我对 run() 总是在 start() 被调用之后立即调用的初始前提是错误的。请帮忙!再次需要的是在 start() 之后立即执行 run()。谢谢。

【问题讨论】:

  • 你可以调用Thread.yield()为其他线程“释放”CPU,但这并不意味着你的新线程就是下一个。

标签: java multithreading runnable


【解决方案1】:

嗯...run() 方法将在不同的线程中运行。根据定义,这意味着您不能任何假设它将在当前线程中执行哪些语句之前或之后,除非您明确同步它们。

【讨论】:

    【解决方案2】:

    run() 是代码中新线程首先要做的事情,但是新线程首先要做一些设置工作,并且不能保证新线程会完成任何大量工作在原始线程继续调用doSomethingElse()之前。

    您认为这里没有保证是正确的。对多线程代码的行为做出假设是痛苦的根源——尽量不要这样做!

    【讨论】:

    • +1 “对多线程代码的行为做出假设是痛苦的根源——尽量不要这样做!”
    【解决方案3】:

    现在,我的理解是,当 start() 被调用时,JVM 会隐式(并立即)调用 run() 方法...

    这是不正确的。它确实隐式调用run(),但调用不一定立即发生。

    现实情况是,在调用start() 之后,新线程可以在某个时间点被调度。实际调度取决于本机调度程序。它可能会立即发生,或者父线程可能会在子线程被调度之前继续运行一段时间。

    要强制你的线程立即开始运行(或者更准确地说,在doSomethingElse()之前开始运行),你需要做一些显式同步;例如像这样:

        java.util.concurrent.CountDownLatch latch = new CountdownLatch(1);
        new Thread(new MyRunnable(latch)).start();
        latch.await(); // waits until released by the child thread.
        doSomethingElse();
    

    在哪里

    class MyRunnable implements Runnable {
        private CountDownLatch latch;
        MyRunnable (CountDownLatch latch) { this.latch = latch; }
        public void run() {
            doSomeStuff();
            latch.countDown(); // releases the parent thread
            doSomeMoreStuff();
        }
        ...
    }
    

    还有其他方法可以使用并发类或 Java 的互斥/等待/通知原语实现同步1。但是两个线程之间的显式同步是保证您需要的行为的唯一方法。

    请注意,子线程中的doSomething()调用会在父线程释放之前完成,但是doSomethingElese()doSomeMoreStuff()的执行顺序我们无话可说。 (一个可能在另一个之前运行,反之亦然,或者它们可能并行运行。)


    1 - 不推荐使用 wait / notify,但如果并发 API 不可用,它可能是您唯一的选择;例如在 Java ME 上。

    【讨论】:

      【解决方案4】:

      当您调用myThread.start() 时,您的线程就可以执行了。它是否会真正获得 CPU,以及持续多长时间——这取决于操作系统调度程序。事实上,您的run() 可能会立即获得控制权,但在它可以做任何您能注意到的事情之前就失去了控制权。确保您的线程在doSomethingElse() 之前执行所需的唯一方法是使用显式同步。

      【讨论】:

        【解决方案5】:

        您已经开始了一个新线程。该线程与启动它的线程并行运行,因此顺序可能是:

        pObject.run();
        doSomethingElse();
        

        doSomethingElse();
        pObject.run();
        

        或者,更有可能的是,会有一些交叉。 pObject.run() 可能在doSomethingElse() 中间运行,反之亦然,或者一个将在另一个完成之前开始,依此类推。理解这一点并理解原子操作的含义很重要,否则您会发现自己遇到了一些非常难以发现的错误。

        如果两个或多个线程访问相同的变量,情况会更加复杂。在某些情况下,一个线程中的值可能永远不会在一个线程中更新。

        我强烈建议:

        1. 不要让你的程序多线程,除非你绝对需要;和

        2. 如果您愿意,请购买并从头到尾阅读 Brian Goetz 的 Java Concurrency in Practice

        【讨论】:

        • “该线程与启动它的线程并行运行......”。不必要。例如,在一个只有一个内核(并且没有超线程)的平台上,两个线程的执行可能是交错的,但它们不可能并行运行。
        【解决方案6】:

        在线程对象上调用 start 方法可能不会使 jvm 立即调用 run() 方法,而是使线程可运行并准备好执行,在这种情况下,父线程首先执行其代码,然后传递控制权对于子线程,如果您希望子线程在父线程代码执行之前执行,请使用父线程中的 chileThreadObject.join() 方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-11
          • 2011-02-05
          • 2018-05-30
          • 2022-07-08
          • 2011-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多