Java 多線程入門:第一個多線程程序
在 Java 中,多線程編程是非常重要的一部分。本篇文章將通過示例,帶你快速了解如何創建第一個多線程程序,并深入分析其運行機制。
1. 創建一個線程類并繼承 Thread
在 Java 中,我們可以通過繼承 Thread
類并重寫其中的 run()
方法,來定義一個自己的線程行為。
來看第一個示例:
package thread.test;//1.創建一個自己的類,繼承自這個Thread
class MyThread extends Thread {@Overridepublic void run() {//run方法就是這個線程的入口方法,類似于main()System.out.println("hello world");}
}public class ThreadDemo1 {public static void main(String[] args) {//2.根據自定義的類創建出實例(線程實例才是真正的線程)//也可以用MyThread t=new MyThread();Thread thread=new MyThread();//3.調用Thread的start方法,才會真正調用系統api,在系統內核中創建出線程//使用Thread會創建出線程,而直接使用run的話就不會thread.start();}
}
運行結果
2. 代碼分析
當你調用 thread.start()
時,真正開辟了一個新的線程,系統會去執行 MyThread
類的 run()
方法里的代碼(即打印 "hello world")。
注意,調用的是 start()
方法,而不是直接調用 run()
,這是兩者最關鍵的區別!
-
start()
方法通知系統啟動一個新線程,不會阻塞當前主線程。 -
run()
方法只是一個普通的方法調用,不會開啟新的線程。
所以,main()
方法會快速執行完,而子線程仍在后臺執行。主線程和子線程各自獨立運行。
3. 再舉一個例子:多線程并發執行
來看另一個簡單示例,理解并發執行的效果:
public class Example {public static void main(String[] args) {MyThread t = new MyThread();t.start();System.out.println("main線程結束了");}
}
class MyThread extends Thread {@Overridepublic void run() {System.out.println("我是子線程");}
}
可能輸出結果是:
也可能輸出的結果是:
為什么會有兩種可能??
因為多線程執行是并發的,誰先執行完是不確定的,由操作系統線程調度器決定。
4. 小知識:守護線程(Daemon Thread)
在 Java 中,普通線程會阻止整個程序結束。而守護線程不會。
整個進程(整個程序)是不是結束,要看有沒有別的 非守護線程 還在運行!
具體來說:
-
如果還有其他普通線程(非守護線程)在運行,進程不會結束。
-
只有當所有非守護線程都結束以后,整個 Java 進程才真正結束。
-
守護線程(daemon thread)不會阻止進程結束(守護線程就像后臺服務一樣,進程結束了它也跟著掛了)。
如果你希望讓子線程是“守護線程”,可以這樣寫:
MyThread thread = new MyThread();
thread.setDaemon(true); // 設置成守護線程
thread.start();
這樣,當主線程執行完畢后,即使子線程還沒跑完,整個進程也會直接結束!
注意:setDaemon(true)
必須在 start()
之前調用,否則會拋異常!
Java 多線程入門:第二個多線程程序
接下來,我們來寫一個持續運行的線程,看看主線程和子線程如何同時運行、輪流輸出內容。
5. 代碼示例:兩個線程同時輸出內容
package thread.test;class MyThread2 extends Thread {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class ThreadDemo2 {public static void main(String[] args) {Thread t=new MyThread2();t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
執行結果
代碼分析
6. 代碼分析
-
MyThread2
是一個自定義線程類,重寫了run()
方法。 -
子線程每隔 1 秒輸出一次
"hello thread"
。 -
主線程(
main
方法)每隔 1 秒輸出一次"hello main"
。
運行效果示例:
由于是兩個獨立的線程,它們的輸出順序和精確時間點可能不一樣,比如:
??? 有時先看到 "hello main"
??? 有時先看到 "hello thread"
這種不確定性就是并發執行的本質特征!