欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程start方法和run方法的区别

程序员文章站 2024-02-11 21:21:04
...


在进入文章主题之前我们先来说一下Thread和Runnable是什么关系

一、Thread和Runnable是什么关系

  • Thread是一个类,Runnable是一个接口
public interface Runnable {
    // 这里面就一个抽象方法
    public abstract void run();
}
  • Thread实现了Runnable接口,使得run支持多线程
  • 因类的单一继承原则,推荐多使用Runnable接口

我们进行一个测试,分别是直接继承Thread类和实现Runnable接口

1、继承Thread类

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 17:24
 */
public class MyThread extends Thread{
    private String name;

    public MyThread(String name){
        this.name = name;
    }

    @Override
    public void run(){
        for (int i = 0; i < 10 ; i ++) {
            System.out.println("Thread start :" + this.name + ", i = " + i);
        }
    }

}
package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 17:30
 */
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mtl1 = new MyThread("Thread1");
        MyThread mtl2 = new MyThread("Thread2");
        MyThread mtl3 = new MyThread("Thread3");
        mtl1.start();
        mtl2.start();
        mtl3.start();

    }
}

执行结果:
线程start方法和run方法的区别
我们可以看到结果有2有1有3,顺序是不定的,如果顺序是1、2、3,可以多执行几次,毕竟可能执行速度过快,出现上边的乱序则说明,实现了多线程,在线程1没有执行完的时候,执行了线程2或者是3。

接下来测试Runnable:

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 17:34
 */
public class MyRunnable implements Runnable{

    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10 ; i ++) {
            System.out.println("Thread start :" + this.name + ", i = " + i);
        }
    }
}

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 17:36
 */
public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr1 = new MyRunnable("Runnable1");
        MyRunnable mr2 = new MyRunnable("Runnable2");
        MyRunnable mr3 = new MyRunnable("Runnable3");
        Thread t1 = new Thread(mr1);
        Thread t2 = new Thread(mr2);
        Thread t3 = new Thread(mr3);
        t1.start();
        t2.start();
        t3.start();
    }
}

解释一下这里为什么还要新建Thread 对象,因为Runnable只是个接口,只有一个run抽象方法,单单靠这些没有办法实现多线程的,Thread中有一个public Thread(Runnable target)构造函数,可以传入Ruannable实例来实现多线程

结果如下:
线程start方法和run方法的区别

二、线程start方法和run方法的区别

通过上边的介绍,想必对run()方法有一些认识了,一般它被我们用来在线程中执行我们的业务逻辑,也称线程体。
那么start()呢?start()是用来启动一个线程的,执行该方法之后,线程就会处于就绪状态(可执行状态)。附上一张线程状态图:
线程start方法和run方法的区别
那么这两个方法有什么区别呢?

  • 像前边说的一个是启动线程,一个是执行业务代码的
  • 调用start()方法会创建一个新的子线程并启动
  • run()方法只是Thread的一个普通方法的调用,还是会在当前线程下调用执行

对此我们可以测试一下:

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 16:27
 */
public class ThreadTest {
    private static void attack() {
        System.out.println("Hello");
        System.out.println("当前线程为: " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread t = new Thread(){
            public void run(){
                attack();
            }
        };
        System.out.println("当前主线程为 : " + Thread.currentThread().getName());
        t.run();
    }
}

// 运行结果
当前主线程为 : main
Hello
当前线程为: main

我们换成start()试试:

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-03 16:27
 */
public class ThreadTest {
    private static void attack() {
        System.out.println("Hello");
        System.out.println("当前线程为: " + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        Thread t = new Thread(){
            public void run(){
                attack();
            }
        };
        System.out.println("当前主线程为 : " + Thread.currentThread().getName());
        t.start();
    }
}

// 运行结果
当前主线程为 : main
Hello
当前线程为: Thread-0

可以看到start()执行的时候是创建了一个新的线程然后启动并调用方法,我们可以看看Thread源码找到start():

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

在started = true之前有一个start0(),这个会新建一个线程,我们进入start0():

private native void start0();

可以看到是native方法,可以去openjdk查看:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/f54e9b7c1036/src/share/native/java/lang/Thread.c

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
    {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
    {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
    {"resume0",          "()V",        (void *)&JVM_ResumeThread},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield",            "()V",        (void *)&JVM_Yield},
    {"sleep",            "(J)V",       (void *)&JVM_Sleep},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};

第一行就可以看到start0(),它调用的是JVM_StartThread方法,用于创建线程,引自于jvm.h,那我们去找一下jvm.cpp:http://hg.openjdk.java.net/jdk8u/hs-dev/hotspot/file/ae5624088d86/src/share/vm/prims,在这里有一个jvm.cpp:
线程start方法和run方法的区别
在其中我们可以找到:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  // We cannot hold the Threads_lock when we throw an exception,
  // due to rank ordering issues. Example:  we might need to grab the
  // Heap_lock while we construct the exception.
  bool throw_illegal_thread_state = false;

  // We must release the Threads_lock before we can post a jvmti event
  // in Thread::start.
  {
    // Ensure that the C++ Thread and OSThread structures aren't freed before
    // we operate.
    MutexLocker mu(Threads_lock);

    // Since JDK 5 the java.lang.Thread threadStatus is used to prevent
    // re-starting an already started thread, so we should usually find
    // that the JavaThread is null. However for a JNI attached thread
    // there is a small window between the Thread object being created
    // (with its JavaThread set) and the update to its threadStatus, so we
    // have to check for this
    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running

      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      // Allocate the C++ Thread structure and create the native thread.  The
      // stack size retrieved from java is signed, but the constructor takes
      // size_t (an unsigned type), so avoid passing negative values which would
      // result in really large stacks.
      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);

      // At this point it may be possible that no osthread was created for the
      // JavaThread due to lack of memory. Check for this situation and throw
      // an exception if necessary. Eventually we may want to change this so
      // that we only grab the lock if the thread was created successfully -
      // then we can also do this check and throw the exception in the
      // JavaThread constructor.
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  Thread::start(native_thread);

重点看这一句:

 native_thread = new JavaThread(&thread_entry, sz);

创建一个新的线程,传thread_entry,我们调到这个方法:

static void thread_entry(JavaThread* thread, TRAPS) {
  HandleMark hm(THREAD);
  Handle obj(THREAD, thread->threadObj());
  JavaValue result(T_VOID);
  JavaCalls::call_virtual(&result,
                          obj,
                          KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                          vmSymbols::run_method_name(),
                          vmSymbols::void_method_signature(),
                          THREAD);
}
JavaCalls::call_virtual

它会call虚拟机然后去run我们这个新建的线程。具体start()即之后的流程如下:
线程start方法和run方法的区别

以上纯为个人理解,如有不对,请各位看官及时指出(轻喷)

相关标签: Java底层