尝试从源码分析start与run的区别,Thread与Runnable的区别
start与run
先来看两个程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package indi.greenhat.thread;
public class Thread1 {
private static void attack(){ System.out.println("attack"); System.out.println("Current Thread is: " + Thread.currentThread().getName()); }
public static void main(String[] args) { Thread t = new Thread(){
@Override public void run(){ attack(); } };
System.out.println("current main thread is: " + Thread.currentThread().getName()); t.run(); } }
|
程序输出:
1 2 3
| current main thread is: main attack Current Thread is: main
|
然后启动线程那里,改为
程序输出:
1 2 3
| current main thread is: main attack Current Thread is: Thread-0
|
从程序运行的结果很简单的看出,使用run()
方法会使用主线程来运行,而start()
方法则使用非主线程来运行
start
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public synchronized void start() {
if (threadStatus != 0) throw new IllegalThreadStateException();
group.add(this);
boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
|
再看该方法里面的start0()
方法
1 2 3 4 5 6 7 8 9 10 11 12
| private native void start0();
@Override public void run() { if (target != null) { target.run(); } }
|
start0()
是native
方法,我们可以查看openjdk
里面对应的实现Thread.c
1 2 3 4
| ... static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, ...
|
可以看到,它是调用的JVM
的JVM_StartThread
方法
然后再去看对应的源码jvm.cpp
1 2 3 4 5 6 7
| ... JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_StartThread"); JavaThread *native_thread = NULL; ... native_thread = new JavaThread(&thread_entry, sz); ...
|
可以看到,会通过new JavaThread
方法创建一个新的线程,再看其形参thread_entry
1 2 3 4 5 6 7 8 9 10 11
| 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); }
|
可以看到thread_entry
会通过让虚拟机创建一个线程,然后去执行run()
方法里面的内容
总结
从上面的源码执行过程来看,它们的调用如下:
调用start()
方法会创建一个新的子线程并启动
run()
方法只是Thread
的一个普通方法调用
Thread与Runnable
Thread
是一个类,其实现了Runnable
接口
1 2 3 4
| public class Thread implements Runnable { ... }
|
1 2 3 4 5 6 7 8 9 10 11 12
| public interface Runnable {
public abstract void run(); }
|
上面看来,Runnable
接口并没有具备多线程的特性,是依赖Thread
里面的start()
方法去创建一个子线程
看下面例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package indi.greenhat.thread;
public class Thread2 extends Thread{ private String name; public Thread2(String name){ this.name = name; }
@Override public void run(){ for(int i = 0; i < 4; i++){ System.out.println("Thread start: " + this.name + ",i: " + i); } }
public static void main(String[] args) { Thread2 th1 = new Thread2("threa1"); Thread2 th2 = new Thread2("threa2"); Thread2 th3 = new Thread2("threa3"); th1.start(); th2.start(); th3.start(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12
| Thread start: threa1,i: 0 Thread start: threa1,i: 1 Thread start: threa1,i: 2 Thread start: threa1,i: 3 Thread start: threa2,i: 0 Thread start: threa2,i: 1 Thread start: threa2,i: 2 Thread start: threa2,i: 3 Thread start: threa3,i: 0 Thread start: threa3,i: 1 Thread start: threa3,i: 2 Thread start: threa3,i: 3
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package indi.greenhat.thread;
public class MyRunnable implements Runnable{ private String name; public MyRunnable(String name){ this.name = name; }
@Override public void run(){ for(int i = 0; i < 4; i++){ System.out.println("MyRunnable start: " + this.name + ",i: " + i); } }
public static void main(String[] args) { MyRunnable th1 = new MyRunnable("threa1"); MyRunnable th2 = new MyRunnable("threa2"); MyRunnable th3 = new MyRunnable("threa3"); Thread t1 = new Thread(th1); Thread t2 = new Thread(th2); Thread t3 = new Thread(th3); t1.start(); t2.start(); t3.start(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12
| Thread start: threa3,i: 0 Thread start: threa3,i: 1 Thread start: threa3,i: 2 Thread start: threa3,i: 3 Thread start: threa1,i: 0 Thread start: threa1,i: 1 Thread start: threa1,i: 2 Thread start: threa1,i: 3 Thread start: threa2,i: 0 Thread start: threa2,i: 1 Thread start: threa2,i: 2 Thread start: threa2,i: 3
|
总结:
Thread
是实现了Runnable
接口的类,使得run
支持多线程
- 因类的单一继承原则,推荐多使用
Runnable
接口
参考:
同一个线程多次调用start()会出现的问题 - 风止雨歇 - 博客园