啓動Java線程
public class StartAndRunMethod {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println(Thread.currentThread().getName());
};
task.run();
new Thread(task).start();
}
}
運行結果:
main
Thread-0
上面的語句無論是task.run(),還是new Thread(task).start(),都是由main線程來執行的。但Runnable對象run方法內容分別由main線程和子線程執行。
小結:調用start方法纔是真正意義上的啓動線程,經歷線程的生命週期;而run方法只是一個普通方法,直接調用不會使用子線程來運行。
start方法底層實現
調用start方法會告訴JVM在合適時候啓動線程,運行run方法。
start方法涉及到兩個線程:父線程(用於創建子線程)和子線程。啓動子線程需要做一些準備工作,例如獲取到除了CPU之外的所有資源,例如上下文、棧、線程狀態等。
// java.lang.Thread
public synchronized void start() {
// 0狀態對應線程的NEW狀態
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 通知線程組當前線程將要啓動:當前線程新增到線程組的線程列表,未啓動線程數減1
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 */
}
}
}
// JNI方法
private native void start0();
start方法的執行流程
- 檢查線程狀態,只有NEW狀態才能繼續,否則拋出IllegalThreadStateException(非法線程狀態異常);
- 當前線程加入線程組
- 調用Java本地方法start0()啓動線程。
注意點:
- start方法由synchronized修飾,是線程安全的。
- 由JVM創建的main方法線程和system組線程,不會通過start方法啓動。
- 線程終止(Terminated)後,不允許再回到Runnable狀態。
run方法底層實現
如果當前線程是通過實現Runnable接口方式創建,會調用Runnable對象的run方法;否則不做任何事情。如果通過繼承Thread方式創建線程,需要重寫run方法。
public void run() {
if (target != null) {
target.run();
}
}
示例:代碼同時使用兩種方式
匿名內部類實現Runnable接口,並在類內部重寫run方法。重寫run方法的代碼直接覆蓋Thread類run方法代碼,Runnable中run方法實現代碼不會執行。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用Runnable方式創建線程");
}
}) {
@Override
public void run() {
System.out.println("使用Thread方式創建線程");
}
};
thread.start();