線程Thread類的start()方法和run()方法
java的線程是通過java.lang.Thread
類來實現的。VM啟動時會有一個由主方法所定義的線程。可以通過創建Thread的實例來創建新的線程。每個線程都是通過某個特定Thread對象所對應的方法run()
來完成其操作的,方法run()稱為線程體。通過調用Thread類的start()
方法來啟動一個線程。
在Java當中,線程通常都有五種狀態,創建、就緒、運行、阻塞和死亡。
第一是創建狀態。在生成線程對象,並沒有調用該對象的start方法,這是線程處於創建狀態。
第二是就緒狀態。當調用了線程對象的start方法之後,該線程就進入了就緒狀態,但是此時線程調度程序還沒有把該線程設置為當前線程,此時處於就緒狀態。在線程運行之後,從等待或者睡眠中回來之後,也會處於就緒狀態。
第三是運行狀態。線程調度程序將處於就緒狀態的線程設置為當前線程,此時線程就進入了運行狀態,開始運行run函數當中的代碼。
第四是阻塞狀態。線程正在運行的時候,被暫停,通常是為了等待某個時間的發生(比如說某項資源就緒)之後再繼續運行。sleep,suspend,wait等方法都可以導致線程阻塞。
第五是死亡狀態。如果一個線程的run方法執行結束或者調用stop方法後,該線程就會死亡。對於已經死亡的線程,無法再使用start方法令其進入就緒。
二、start()方法
1、為什麽需要start方法;它的作用是什麽?
start()方法來啟動線程,真正實現了多線程運行。
start方法的作用就是將線程由NEW狀態,變為RUNABLE狀態。當線程創建成功時,線程處於NEW(新建)狀態,如果你不調用start( )方法,那麽線程永遠處於NEW狀態。調用start( )後,才會變為RUNABLE狀態,線程才可以運行。
2、調用start()方法後,線程是不是馬上執行?
線程不是馬上執行的;準確來說,調用start( )方法後,線程的狀態是“READY(就緒)”狀態,而不是“RUNNING(運行中)”狀態(關於線程的狀態詳細。線程要等待CPU調度,不同的JVM有不同的調度算法,線程何時被調度是未知的。因此,start()方法的被調用順序不能決定線程的執行順序
註意:
由於在線程的生命周期中,線程的狀態由NEW ----> RUNABLE只會發生一次,因此,一個線程只能調用start()方法一次,多次啟動一個線程是非法的。特別是當線程已經結束執行後,不能再重新啟動。
三、run( )方法
1、run方法又是一個什麽樣的方法?run方法與start方法有什麽關聯?
run()方法當作普通方法的方式調用
run( )其實是一個普通方法,只不過當線程調用了start( )方法後,一旦線程被CPU調度,處於運行狀態,那麽線程才會去調用這個run()方法;
2、run()方法的執行是不是需要線程調用start()方法
上面說了,run()方法是一個普通的對象方法,因此,不需要線程調用start()後才可以調用的。可以線程對象可以隨時隨地調用run方法。
Example1:
Thread t1 = new Thread(new MyTask(1));
Thread t2 = new Thread(new MyTask(2));
t1.run();
t2.run();
上面的輸出結果是固定的:
count的值:1
count的值:2
再看另一個實例:
Thread t1 = new Thread(new MyTask());
Thread t2 = new Thread(new MyTask());
t1.start();
t2.start();
這個輸出結果不是固定的,因為線程的運行沒法預測。運行結果可能不一樣。
MyTask 類:
//實現Runnable接口
class MyTask implements Runnable{
int count;
public MyTask(int count) {
this.count=count;
}
@Override
public void run() {
System.out.println("count的值:"+count);
}
}
Example2:
1、用start方法啟動線程
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.start();
t2.start();
}
}
class T1 implements Runnable {
public void run() {
try {
for(int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(100); //模擬耗時任務
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class T2 implements Runnable {
public void run() {
try {
for(int i=0;i>-10;i--){
System.out.println(i);
Thread.sleep(100); //模擬耗時任務
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果:
說明兩線程是並發執行的。
2、先用run方法啟動線程
將上面的start()改為run()
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new T1());
Thread t2 = new Thread(new T2());
t1.run();
t2.run();
}
}
說明兩線程實際是順序執行的。
總結:
通過實例1和實例和我們可以知道start方法是用於啟動線程的,可以實現並發,而run方法只是一個普通方法,是不能實現並發的,只是在並發執行的時候會調用。
說到這,不知道小夥伴們有沒有明白這兩個方法的區別,如果還有疑問,可以留言交流。
四、start()方法和run()方法源碼解析(基於JDK1.7.0_40)
public synchronized void start() {
// 如果線程不是"就緒狀態",則拋出異常!
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 將線程添加到ThreadGroup中
group.add(this);
boolean started = false;
try {
// 通過start0()啟動線程,新線程會調用run()方法
start0();
// 設置started標記=true
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
public void run() {
if (target != null) {
target.run();
}
}
五、真正理解Thread類
Thread類的對象其實也是一個java對象,只不過每一個Thread類的對象對應著一個線程。Thread類的對象就是提供給用戶用於操作線程、獲取線程的信息。真正的底層線程用戶是看不到的了。
因此,當一個線程結束了,死掉了,對應的Thread的對象仍能調用,除了start( )方法外的所有方法(死亡的線程不能再次啟動),如run( )、getName( )、getPriority()等等
//簡單起見,使用匿名內部類的方法來創建線程
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("Thread對象的run方法被執行了");
}
};
//線程啟動
thread.start();
//用循環去監聽線程thread是否還活著,只有當線程thread已經結束了,才跳出循環
while(thread.isAlive()){}
//線程thread結束了,但仍能調用thread對象的大部分方法
System.out.println("線程"+thread.getName()+"的狀態:"+thread.getState()+"---優先級:"+thread.getPriority());
//調用run方法
thread.run();
//當線程結束時,start方法不能調用,下面的方法將會拋出異常
thread.start();
參考資料
- http://www.cnblogs.com/jinggod/p/8485143.html
- https://blog.csdn.net/u010568463/article/details/47911181
- https://blog.csdn.net/xuxurui007/article/details/7685076
文章有不當之處,歡迎指正,你也可以關註我的微信公眾號:
好好學java
,獲取優質學習資源。
線程Thread類的start()方法和run()方法