相关文章:
在平常使用线程的过程中,我们可定会有这么一个疑问,启用线程时使用 start() 方法而不是 run() 方法,这两个方法之间究竟有什么区别呢,这里让我们来一起深入了解下
一、举例说明
-
例子一 (调用 run() 方法)
public class CurrentThreadTest { private static void attack() { System.out.println("Fight"); System.out.println("Current Thread is: " + Thread.currentThread().getName()); } public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { attack(); } }; System.out.println("Current Thread is: " + Thread.currentThread().getName()); thread.run(); } } // Current Thread is: main // Fight // Current Thread is: main
-
如上所示,我们定义了一个 attack(),并打印出执行该方法的线程名称,另外在 main() 方法中 new 了一个线程,打印出执行 main() 方法的线程名称,并调用 run() 方法
-
由输出可知,执行 main() 方法和 attack() 方法的为同一个线程,即主线程 main
-
-
例子二 (调用 start() 方法)
public class CurrentThreadTest { private static void attack() { System.out.println("Fight"); System.out.println("Current Thread is: " + Thread.currentThread().getName()); } public static void main(String[] args) { Thread thread = new Thread() { @Override public void run() { attack(); } }; System.out.println("Current Thread is: " + Thread.currentThread().getName()); thread.start(); } } // Current Thread is: main // Fight // Current Thread is: Thread-0
- 如上所示,调用了线程的 start() 方法,由输出可知,执行 main() 方法和 attack() 方法的不是同一个线程,也就是说当调用 start() 时,会创建一个新的线程来调用 attack() 方法
二、源码解析
-
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 */ } } } private native void start0();
- 源码如上,可知 start() 方法中主要调用的方法为 start0() 方法,这是一个本地方法,我们可以通过 openjdk 来查看其源码 (该方法位于 Thread.c 类下)
-
Thread.c --> 传送门
#include "jni.h" #include "jvm.h" #include "java_lang_Thread.h" #define THD "Ljava/lang/Thread;" #define OBJ "Ljava/lang/Object;" #define STE "Ljava/lang/StackTraceElement;" #define STR "Ljava/lang/String;" #define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) 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 的源码
-
jvm.cpp --> 传送门
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);
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); }
-
源码如上,可知 JVM_StartThread() 方法中 new 了一个 Java 线程,该方法其中一个参数方法: thread_entry(),在 thread_entry() 方法中,会调用虚拟机并传入 run() 方法的名字
-
也就是说,最终 new 了一个线程,然后用这个线程去执行 run() 方法里面的内容
-
三、归纳总结
-
调用 start() 方法会创建一个新的子线程并启动
-
调用 run() 方法只不过是调用了 Thread 类的一个普通方法