先給結論:
-
start()屬於Thread本身,run()方法實現於Runnable接口;
-
調用start()方法時會調用native的start0()方法,啓用一個線程,在獲取到CPU時間片時調用run()方法;直接調用run()方法,僅僅是普通方法調用,並不會啓用線程;
-
start()方法是啓用一個線程,所以不會阻塞調用它的線程;run()方法不會啓用線程,所以會阻塞調用它的線程,直至run()方法體執行結束;
-
run()方法一般可以被多次調用,start()是synchronized修飾的,在調用start0()方法前會確認當前線程狀態,如果已經被啓用,將會拋出異常。
一、start()和run()方法的源碼
start()方法源碼:
public synchronized void start() {
// 判斷線程狀態,0爲NEW
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 將線程添加到ThreadGroup組中
group.add(this);
boolean started = false;
try {
// 調用native方法start0(),當cpu空閒時將會執行此線程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
run()方法源碼:
// 實現runnable接口的run()
@Override
public void run() {
if (target != null) {
target.run();
}
}
二、驗證是否創建線程與執行順序
上代碼:
public class ThreadDemo extends Thread{
// 線程體,run()方法運行完,這個線程隨即終止
public void run() {
// 打印當前正在執行的線程名稱
System.out.println("當前線程名稱:" + Thread.currentThread().getName());// 2
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
// 回調Runnable接口的run()方法
threadDemo.run();// 1
System.out.println("main 當前線程名稱:" + Thread.currentThread().getName());// 3
ThreadDemo threadDemo1 = new ThreadDemo();
// 調用Thread的start()方法
threadDemo1.start();// 4
System.out.println("this is test");// 5
}
}
執行結果:
當前線程名稱:main
main 當前線程名稱:main
this is test
當前線程名稱:Thread-1
顯然,當1處的run()方法被調用時,執行了2處的打印語句,而3處的打印語句則是等待1調用的run()執行結束才執行;而4處的start()方法也調用了2處的打印語句,卻在5之後輸出了,並且線程名稱不是main。
分析:當main線程調用run()方法時,與調用普通方法一樣,等run()方法體執行結束才繼續執行下面的語句(阻塞了之後的語句的執行);main線程調用start()方法時,啓用了另一個線程Thread-1,但是由於當前main線程佔用着CPU,所以Thread-1沒有立即執行,而是等5處的打印語句執行完(此時main方法執行完畢,讓出了CPU)才執行(未阻塞main線程)。【實際上這裏的”佔用“、”讓出“並不正確!!!見第三節】
三、利用sleep()方法"阻塞"main線程
首先要爲CPU正名:並不是誰想讓老子執行,老子就執行的
修改代碼:
public class ThreadDemo extends Thread{
// 線程體,run()方法運行完,這個線程隨即終止
public void run() {
// 來一個耗時的循環
int i = 0;
while (i < Integer.MAX_VALUE) {
i++;
}
System.out.println("當前線程名稱:" + Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo1 = new ThreadDemo();
// 調用Thread的start()方法
threadDemo1.start();
Thread.sleep(1);// 6
// Thread.sleep(1000); // 7
System.out.println("this is test");// 8
}
}
先註釋7處的代碼,執行結果爲:
this is test
當前線程名稱:Thread-0
Thread-0的輸出在打印語句8的後面。這意味着CPU在執行main線程時並沒有等start()方法執行完就執行了8,這似乎與第二節中的表述不符啊???不急,繼續看。
當我們註釋6處的代碼,放開7處的代碼時,執行結果爲:
當前線程名稱:Thread-0
this is test
可以發現:main線程多睡了999ms,結果被Thread-0插隊了,爲什麼會這樣?其實是CPU在搞鬼,首先現在的CPU並非單核,不是單核就可以利用其他的核來”乾點別的“;其次,假設上述代碼6處執行前,Thread-0因爲沒有CPU執行而處於等待狀態,那麼在6中的sleep語句執行完那一刻將會有兩個線程(main和Thread-0)等着要被執行,那麼這個時候CPU該去執行誰呢:
CPU調度規則,順序隨機
說白了,這個時候得先執行main還是先執行Thread-0得看CPU臉色;這裏涉及到CPU的調度規則等相關內容,不是很熟悉,暫不展開,有興趣可以自行搜索。