一、前言
不知道哪位古人說:人生三大境界。第一境界是:看山是山看水是水;第二境界是看山不是山看水不是水;第三境界:看山還是山看水還是水。
其實我想對於任何一門技術的學習都是這樣。
形而上下者為之器,形而上者為之道。一直很喜歡自己大一的高數老師,老師是老教授了,他講數學,會引申到建築學,計算機科學,以及哲學再到生活中的常識。也能從其他學科、日常生活中,提取出數學的概念。我想,這就是形而上者了。
不勝望之
不多言,這里我們來深入java底層,看下java表皮之下的筋肉以及內臟。
二、從一段代碼展開
package thread;
/**
* @author xuyuanpeng
* @version 1.0
* @date 2019-05-17 17:04
*/
public class ThreadMain {
public static void main(String[] args) {
Thread thread=new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2=new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
log("線程1開始");
thread.start();
log("線程1結束");
log("線程2開始");
t2.run();
log("線程2結束");
}
public static void log(String msg){
System.err.print(System.currentTimeMillis());
System.out.println(">>>"+msg);
}
public static void log(){
log("");
}
}
這里可以思考下輸出的結果:
1
2
3
鐺鐺鐺檔
Connected to the target VM, address: '127.0.0.1:51304', transport: 'socket'
1558085396255>>>線程1開始
1558085396255>>>線程1結束
1558085396255>>>線程2開始
1558085397255>>>線程2結束
Disconnected from the target VM, address: '127.0.0.1:51304', transport: 'socket'
細心的同學肯定已經發現了
線程1是start的方式啟動,而線程2是run方法啟動
差異在哪?
線程1執行start,並沒有阻塞線程
而線程2的run方法,阻塞了線程。何改咯?┓( ´∀` )┏
為什么是這樣的呢?start與run的區別究竟在哪呢?讓我們深入她,張愛玲說,了解一個女人最好的通道就是XX,所以讓我們深入她,再了解她。
三、JDK源碼分析
1、start方法
public synchronized void start() {
/**
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
*
* 1、start方法將導致當前線程開始執行。由JVM調用當前線程的run方法。
*
* 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).
*
* 2、結果是 調用start方法的當前線程 和 執行run方法的另一個線程 同時運行。
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* 3、多次啟動線程永遠不合法。 特別是,線程一旦完成執行就不會重新啟動。
*
* @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.
*
* 4、對於由VM創建/設置的main方法線程或“system”組線程,不會調用此方法。
* 未來添加到此方法的任何新功能可能也必須添加到VM中。
*
* A zero status value corresponds to state "NEW".
* 5、status=0 代表是 status 是 "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.
*
* 6、通知組該線程即將啟動,以便將其添加到線程組的列表中,
* 並且減少線程組的未啟動線程數遞減。
*
* */
group.add(this);
boolean started = false;
try {
//7、調用native方法,底層開啟異步線程,並調用run方法。
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
* 8、忽略異常。 如果start0拋出一個Throwable,它將被傳遞給調用堆棧。
*/
}
}
}
start方法用synchronized修飾,為同步方法;
雖然為同步方法,但不能避免多次調用問題,用threadStatus來記錄線程狀態,如果線程被多次start會拋出異常;threadStatus的狀態由JVM控制。
使用Runnable時,主線程無法捕獲子線程中的異常狀態。線程的異常,應在線程內部解決。
2、native start0方法
private native void start0();
native 是聲明本地方法,在此處是JVM中的方法。
3、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();
}
}
run方法就很簡單了,就是回調了Runable的run()接口
導致Thread寫的@Overwrite void run() 方法直接是在主線程執行,導致阻塞了主線程。
四、總結
到此我們就知道了,start會使重寫的run方法被虛擬機調用,是在子線程中執行的run方法
而直接調用線程的run方法,他是內部回調了run接口,導致直接執行了Runable.run的重寫內容。相當於直接在主線程中執行。