【问题标题】:Java - Call to start method on thread : how does it route to Runnable interface's run ()?Java - 在线程上调用 start 方法:它如何路由到 Runnable 接口的 run()?
【发布时间】:2011-02-05 11:42:55
【问题描述】:

好的,我知道创建新线程并在 Java 中运行它的两种标准方法:

  1. 在类中实现Runnable,定义run()方法,并将类的实例传递给新的Thread。当调用线程实例上的start()方法时,会调用类实例的run方法。

  2. 让类派生自Thread,这样它就可以覆盖方法run(),然后当调用新实例的start()方法时,调用会被路由到被覆盖的方法。

在这两种方法中,基本上都会创建一个新的Thread 对象并调用它的 start 方法。但是,虽然在第二种方法中,调用被路由到用户定义的run() 方法的机制非常清楚,(这是一个简单的运行时多态性),我不明白如何调用start() 方法在 Thread 对象上被路由到实现Runnable 接口的类的run() 方法。 Thread 类是否有一个类型为 Runnable 的私有字段,它首先检查它,如果它被设置然后调用 run 方法,如果它设置为一个对象?这将是一个奇怪的机制 IMO。

线程上对start()的调用如何路由到构造线程时对象作为参数传递的类实现的Runnable接口的run方法?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    Thread 保留对Runnable 实例的引用,并在run 的基本实现中调用它。

    你可以在源码中看到这个:

    // passed into the constructor and set in the init() method
    private Runnable target;
    ...
    // called from native thread code after start() is called
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    

    【讨论】:

    • 大概这通常是对this的引用,就像在第二种方法中一样?
    • 这并没有回答他的问题。如果从 Thread 派生,则 Runnable 引用(在源中称为 target)为 null。
    • @Frederik:是的,但是由于你覆盖了run,它不会有任何事情发生。
    • 没错。但是我只是发现您的回答容易误解,因为您没有直接提及这一点。哦,仅供参考,我没有对你投反对票。编辑:对不起。是我误解了 OP 的问题!
    • 被接受为答案,但看起来这意味着它基于一个非常经验的算法。如果对象恰好是 Thread 的派生类,则将调用覆盖。否则为默认值,它将检查是否设置了引用并调用 run() ...我在想他们为什么这样做..这是任何模式吗?
    【解决方案2】:

    在这两种情况下,都必须有具体的 Thread 类。在第一种情况下(实现 Runnable),使实现它的类能够“成为”一个线程。您仍然必须将您的类作为参数传递给 Thread 类的构造函数。而在扩展线程类的第二种情况下,情况并非如此。

    当调用 start() 方法时,不能保证 run() 方法会立即被调用。调用 start() 方法表示线程已准备好运行。此后它可以进入任何状态,具体取决于线程池。

    仅供参考:class Thread implements Runnable

    【讨论】:

      【解决方案3】:

      您可能刚刚检查了包含在 JDK 中的 Thread.java 的来源:

      public void run() {
          if (target != null) {
              target.run();
          }
      }
      

      目标在哪里:

      private Runnable target;
      

      但我认为您正在寻找的真正答案是线程如何真正工作的详细信息。不幸的是,这在本机库中被抽象掉了。试着对线程的工作原理有一个高层次的了解。

      【讨论】:

        【解决方案4】:

        虽然您可以查看实际的源代码,但猜测应该是这样的:

        public class MyThread implements Runnable {
         private  Runnable r;
         public MyThread() {
         }
         public MyThread(Runnable r) {
                this.r = r;
         }
        
         public void start() {
           //magic to launch a new thread
           run(); // the above magic would probably call run(), rather than
                  // call it directly here though.
          }
          public void run() {
            if(r != null) 
               r.run();
           }
        }
        

        简而言之,如果你扩展 MyThread 并覆盖 run(),你的 run() 方法就会被调用。如果您改为传递一个 Runnable,MyThread 的 run() 方法只会委托给该 Runnable 的 run() 方法。

        【讨论】:

        • 你的猜测完全正确,只是r 被称为target,并且构造函数调用了一个设置它的init 方法。
        【解决方案5】:

        答案尚未探索的一件事是事情如何从start() 变为run(),这既简单又复杂。

        简单来说,start() 方法调用本机方法(在 OpenJDK 实现中为 start0),该方法为新堆栈分配一些内存,并要求操作系统以该空间作为堆栈和普通C++ 函数(OpenJDK 实现中的thread_entry)作为实现函数。该函数反过来将 thunk 回 Java 以调用 Thread 对象上的 run() 方法。在 POSIX 系统或 Windows 中执行本机线程的任何人都应该熟悉低级别的模式(要求操作系统在堆栈上启动一个新线程并使用一个函数)。

        细节使这一切变得更加复杂,需要处理所有错误处理和模糊的边缘情况。如果您好奇,请阅读OpenJDK 资源,特别注意jvm.cpp 中的Thread.javaJVM_StartThread 以及thread.cppthread.hpp 中的JavaThread 类。希望这个答案能给你足够的细节,让你找到自己的方式......

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-03
          相关资源
          最近更新 更多