先看Java開啓Thread的兩種寫法
1. 繼承Thread類,重寫run方法。
class MyThread extends Thread{
@Override
public void run() {
}
}
void startThread(){
MyThread myThread = new MyThread();
myThread.start();
}
2. 實現Runable接口的run方法,並傳入Thread的構造方法。
class MyTask implements Runnable{
@Override
public void run() {
}
}
void startThread(){
MyTask myThread = new MyTask();
Thread thread = new Thread(myThread);
thread.start();
}
很多人會有疑問,這兩種方式開啓線程在底層實現上有差別嗎?
帶着這個問題我們再來看JDK源碼中Thread這個類。
public class Thread implements Runnable {
//省略2000多行代碼...
}
Thread類本身實現了Runable接口。
繼續查看JDK源碼中的Runnable接口是怎麼寫的。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
顯而易見,Runnable接口只有一個方法run()。然後我們查看Thread類是怎麼實現run方法的。
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
而這個target又是什麼?這是Thread類的一個Runnable類型的成員變量,通過搜索發現只有在init()和exit()方法中對這個成員變量進行賦值。
這裏先看init方法
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
}
g.checkAccess();
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
this.target = target;
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
這個方法基本工作就是初始化各個成員變量,通過查找發現Thread的一系列構造方法最終都調用了這個方法,而以上第二種方法在Thread構造方法中傳入的Runnable對象的引用最終就是賦值給了這個成員變量target,會在Thread的run方法中調用Runnable對象的run方法。
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
通過分析init代碼發現,在構建出一個Thread對象時,其實並沒有真正在虛擬機內存空間中給這個線程分配內存。虛擬機真正創建線程是在調用start方法時,通過start方法源碼可以觀察到蛛絲馬跡。
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
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".
*/
// Android-changed: Replace unused threadStatus field with started field.
// The threadStatus field is unused on Android.
if (started)
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);
// Android-changed: Use field instead of local variable.
// It is necessary to remember the state of this across calls to this method so that it
// can throw an IllegalThreadStateException if this method is called on an already
// started thread.
started = false;
try {
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// start0();
nativeCreate(this, stackSize, daemon);
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 */
}
}
}
// Android-changed: Use Android specific nativeCreate() method to create/start thread.
// The upstream native method start0() only takes a reference to this object and so must obtain
// the stack size and daemon status directly from the field whereas Android supplies the values
// explicitly on the method call.
// private native void start0();
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
這裏關注nativeCreate這個方法,這是一個本地方法,具體實現不深入研究,最終通過start中的這個方法開啓一個線程,而真正在線程中執行的代碼就是run方法中根據具體業務實現的代碼。
回答上面的問題:兩種創建線程的方法,區別就是對run方法的處理,第一種方式通過直接繼承重寫來實現,第二種方式則通過傳參組合的方式實現,對線程的最終執行並無本質區別。
任何線程都是Runnable類型,Runnable這個詞用的相當到位,可以理解爲可運行狀態,但不一定是正在運行的,因爲線程是CPU的最小調度單元,CPU的快速調度線程狀態的切換對於用戶幾乎是無感知的。
關於操作系統線程的概念,可以閱讀《操作系統精髓與設計原理》知識梳理 之線程篇。