多任務執行的演變
玩電腦的時候,可以一邊聽歌,一邊聊天,電腦可以同時做很多事,多個任務宏觀上同時在執行。
- 單核CPU時代:
- 一個任務的時候,在一個時間點只能執行一個任務。
- 多個任務的時候,“同一個時間點”執行多個任務,並非真正意義的同個時間點,而是由操作系統不斷切換時間片
(不斷切換任務佔領CPU),讓每個任務都有一定時間可以運行。由於這個時間片特別短,所以我們幾乎感受不出來。
- 多核CUP時代:真正的不同線程可以被不同CPU核執行,也就是可能同時在操作一個內存數據,如果使用不好,
這種行爲導致結果可能是不可預測的。(一個線程A寫操作改了數據,但是另一個線程B不知道A改了數據,
讀取出了這個數據,卻發現不是自己想要的)
線程和進程的區別
-
進程:操作系統資源分配的基本單位,打開電腦的任務管理器就可以看到很多進程,每一個程序(比如QQ)一般不止一個進程。
-
線程:任務調度和執行的基本單位,一個進程可能有很多線程,一個有效的進程至少有一個線程。每一個線程都有自己的局部變量表,
程序計數器(指向正在執行的指令指針)。 -
進程有自己的內存地址空間,線程沒有,被包含在進程的地址空間中。
-
進程最少有一條線程,線程可以看成是輕量級的進程。
-
本質區別:進程擁有自己的一整套變量,而線程則共享數據,撤銷或者啓動一個線程的成本更低。
爲什麼需要多線程
在平時下載東西的時候,我們可能需要同時做點其他東西,加入一個音樂軟件只能有一個線程,那麼下載的時候,我們就不能同時聽音樂了,說更嚴重一些,如果下載的時候網路比較慢,阻塞了,我們想點擊按鈕讓它停下來,也是不能的。
如果一個線程,我們模擬一下聽歌又看書的情景,我們會發現永遠不會輸出看書。
public class TryConcurrent {
public static void main(String[] args) {
listenToMusic();
readBook();
}
public static void listenToMusic(){
for(;;){
System.out.println("I am listening to music!");
}
}
public static void readBook(){
for(;;) {
System.out.println("I am reading a book!");
}
}
}
輸出結果:
I am listening to music!
I am listening to music!
I am listening to music!
I am listening to music!
I am listening to music!
I am listening to music!
...
啓動多線程
於是我們使用new Thread來創建一個新的線程,如下:
public class TryConcurrent {
public static void main(String[] args) {
listenToMusic();
new Thread(new Runnable() {
@Override
public void run() {
readBook();
}
}).start();
}
public static void listenToMusic(){
for(;;){
System.out.println("I am listening to music!");
}
}
public static void readBook(){
for(;;) {
System.out.println("I am reading a book!");
}
}
}
但是這樣也得不到我們想要的結果,也是會一直輸出聽音樂,這是爲什麼呢?明明後面啓動了新的線程了。
上面代碼通過匿名內部類的方式創建線程並且重寫了run方法。
啓動新線程必須在其中一個任務之前,因爲啓動線程本身是依賴於主線程的,也就是主線程的聽音樂一直不結束的話,就永遠執行不到新建線程那一步,自然也就不會有readbook輸出。
其中,調用start方法,纔是真正的派生了一個新的線程,否則只是一個簡單的Thread對象,start方法是一個立即返回的方法,也就是不管線程裏面邏輯多複雜,都是會執行後面的方法的,因爲裏面的邏輯歸新創建的線程,而不是當前線程。
如果使用java 8的Lambda屬性的話,可以直接寫成new Thread(TryConcurrent::readBook).start();
在運行的時候,我們可以使用Jconsole來觀察線程,只要在cmd輸入Jconsole
即可,或者Jstack也是可以的,都是由JDK提供的。
選擇自己要監控的進程,點進去就可以看到線程頁面的一些信息了。
其實我們可以看到並不只有main線程和自己新建的線程Thread-0,還有一些守護線程,比如垃圾回收線程等等。