線程基礎01 1.程式 進程 線程 程式(program):是為完成的特定任務,用某種語言編寫的一組指令的集合。簡單來說,就是我們寫的代碼。 進程: 進程是指運行中的程式,比如我們使用QQ,就啟動了一個進程,操作系統就會為該進程分配空間。當我們使用迅雷,又啟動了一個進程,操作系統將為迅雷分配新的記憶體 ...
線程基礎01
1.程式 進程 線程
- 程式(program):是為完成的特定任務,用某種語言編寫的一組指令的集合。簡單來說,就是我們寫的代碼。
-
進程:
- 進程是指運行中的程式,比如我們使用QQ,就啟動了一個進程,操作系統就會為該進程分配空間。當我們使用迅雷,又啟動了一個進程,操作系統將為迅雷分配新的記憶體空間。
- 進程是程式的一次執行過程,或是正在運行的一個程式。是動態過程:有它自身的產生、存在和消亡的過程。
-
線程:
- 線程是由進程創建的,是進程的一個實體
- 一個進程可以有多個線程,比如:用迅雷同時下載多個文件
-
其他相關概念:
- 單線程:同一時刻,只允許執行一個線程
- 多線程:同一時刻,可以執行多個線程,比如:一個qq進程,可以同時打開多個聊天視窗;一個迅雷進程,可以同時下載多個文件。
- 併發:同一時刻,多個任務交替執行,造成一種“貌似同時”的錯覺,簡單地說,單核cpu實現的多任務就是併發
- 並行:同一時刻,多個任務同時執行。多核cpu可以實現並行。在電腦中也可能同時出現併發和並行的狀態。
例子:
package li.thread;
public class CpuNum {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
//獲取當前的電腦的cpu數量
int cpuNums = runtime.availableProcessors();
System.out.println("當前的CPU數量="+cpuNums);//當前的CPU數量=8
}
}
2.線程的基本使用
- 創建線程的兩種方式
在java中線程來使用有兩種方法:
- 繼承Thread類,重寫run方法
- 實現Runnable介面,重寫run方法
2.1繼承Thread創建線程
例子1:線程應用案例1-繼承Thread類
1)請編寫程式,開啟一個線程,該線程每隔一秒,在控制台輸出 “喵喵,我是小貓咪”
2)對上題改進:當輸出80次“喵喵,我是小貓咪”時,結束該線程
3)使用JConsole監控線程執行情況,並畫出程式示意圖
package li.thread;
//演示通過繼承Thread類創建線程
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
//創建一個Cat對象,可以當做線程來使用
Cat cat = new Cat();
cat.start();//啟動線程
//當main線程啟動一個子線程 Thread-0後,主線程不會阻塞,會繼續執行
//這時 主線程和子線程是交替執行
System.out.println("主線程繼續執行="+Thread.currentThread().getName());//主線程繼續執行=main
for (int i = 0; i < 60; i++) {
System.out.println("主線程 i="+i);
//讓主線程休眠
Thread.sleep(1000);
}
}
}
//1.當一個類繼承了Thread類,該類就可以當做一個線程使用
//2.我們會重寫run方法,寫上自己的業務代碼
//3.run Thread類實現了Runnable介面的run方法
/*
@Override
public void run() {
if (target != null) {
target.run();
}
}
*/
class Cat extends Thread {
@Override
public void run() {//重寫run方法,寫上自己的業務邏輯
int times = 0;
while (true) {
//該線程每隔1秒,在控制台輸出 “喵喵,我是小貓咪”
System.out.println("喵喵,我是小貓咪" + (++times)+" 線程名稱="+Thread.currentThread().getName());
//讓該線程休眠一秒
try {
Thread.sleep(1000);//單位為毫秒 try-catch快捷鍵:Ctrl+Alt+T
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) {
break;//當times到80,退出while,這是線程也就退出了
}
}
}
}
3)使用JConsole監控線程執行情況,並畫出程式示意圖:
如下,在控制台點擊run,運行程式,在程式運行時,點擊Termial
在控制台輸入JConsole,回車。
點擊本地進程,點擊Thread01,點擊下方連接按鈕:
在彈出視窗中點擊不安全的連接按鈕:
在視窗中點擊“線程”:
可以在左下角的線程小視窗中看到main線程和Thread-0線程在同時進行
等待一段時間,可以看到當run視窗的主線程 i = 60之後,main線程結束
結束前:
結束後:
當線程名稱=Thread-0輸出到80次時,雖然可以Thread-0還在左下角,但是實際上Thread-0線程已經結束了,整個進程隨之結束。
程式示意圖:
註意:在多線程編程裡面,並不一定說主線程結束了,整個進行就結束了,等所有線程都結束了,進程才會結束。
2.2為什麼是start?
在2.1的例子中,主方法中定義了cat對象,該對象調用了start方法,start方法會去啟動一個線程,最終會執行Cat 類的run方法。
思考一個問題:既然最終都是要調用run方法,為什麼cat對象還要通過start方法對調用run呢?為什麼不直接調用?
答案: 首先通過 對象.run() 方法 可以執行方法,但是不是使用的多線程的方式,就是一個普通的方法,沒有真正地啟動一個線程。即這時候把run方法執行完畢,才能執行主方法剩下的語句。
如下圖:將cat.start();
改為cat.run();
之後的運行結果:
在run方法執行完之後才執行主方法剩下的語句
那麼在調用start方法時,整個過程到底是什麼樣子的?
點擊start()方法:可以在start方法中看到一個start0()方法:
點擊start0( )方法:可以看到start0是一個本地方法,由 JVM調用,底層是c/c++實現。
再看看run()方法的源碼:可以看到run方法只是簡單的調用了實現類的run,沒有進行任何的多線程處理。
換而言之,Java中真正實現多線程的效果的是start0方法,而不是run方法