1、什么是進程?什么是線程?
進程:進程是程序的基本執行實體;另外一種解釋是進程是一個應用程序(1個進程是一個軟件)。
線程:線程是操作系統能夠進行運算調度的最下單位。它被包含在進程之中,是進程中的實際運作單位;是一個進程中的執行場景/執行單元。
注意:。一個進程可以啟動多個線程
eg.
對于java程序來說,當在DOS命令窗口中輸入:
java HelloWorld 回車之后。會先啟動JVM,而JVM就是一個進程。
JVM再啟動一個主線程調用main方法(main方法就是主線程)。
同時再啟動一個垃圾回收線程負責看護,回收垃圾。
最起碼,現在的java程序中至少有兩個線程并發,一個是 垃圾回收線程,一個是 執行main方法的主線程。
2、進程和線程是什么關系?
進程:可以看做是現實生活當中的公司。
線程:可以看做是公司當中的某個員工。
注意:進程A和進程B的 內存獨立不共享。
魔獸游戲是一個進程
酷狗音樂是一個進程
這兩個進程是獨立的,不共享資源。
線程A和線程B是什么關系?
在java語言中:
線程A和線程B,堆內存 和 方法區 內存共享。但是 棧內存 獨立,一個線程一個棧。
eg.
假設啟動10個線程,會有10個棧空間,每個棧和每個棧之間,互不干擾,各自執行各自的,這就是多線程并發。
eg.
火車站,可以看做是一個進程。
火車站中的每一個售票窗口可以看做是一個線程。
我在窗口1購票,你可以在窗口2購票,你不需要等我,我也不需要等你。所以多線程并發可以提高效率。
java中之所以有多線程機制,目的就是為了 提高程序的處理效率。
3、思考一個問題
使用了多線程機制之后,main方法結束,是不是有可能程序也不會結束?
main方法結束只是主線程結束了,主棧空了,其它的棧(線程)可能還在壓棧彈棧。
4、分析一個問題
對于單核的CPU來說,真的可以做到真正的多線程并發嗎?
對于多核的CPU電腦來說,真正的多線程并發是沒問題的。4核CPU表示同一個時間點上,可以真正的有4個進程并發執行。
單核的CPU表示只有一個大腦:
不能夠做到真正的多線程并發,但是可以做到給人一種“多線程并發”的感覺。
對于單核的CPU來說,在某一個時間點上實際上只能處理一件事情,但是由于CPU的處理速度極快,多個線程之間頻繁切換執行,給別人的感覺是:多個事情同時在做!!!
eg.
線程A:播放音樂
線程B:運行魔獸游戲
線程A和線程B頻繁切換執行,人類會感覺音樂一直在播放,游戲一直在運行,
給我們的感覺是同時并發的。(因為計算機的速度很快,我們人的眼睛很慢,所以才會感覺是多線程!)
5、什么是真正的多線程并發?
t1線程執行t1的。
t2線程執行t2的。
t1不會影響t2,t2也不會影響t1。這叫做真正的多線程并發。
關于線程對象的生命周期(附圖)?★★★★★
- 新建狀態
- 就緒狀態
- 運行狀態
- 阻塞狀態
- 死亡狀態
線程構造方法
構造方法名 | 備注 |
---|---|
Thread() | |
Thread(String name) | name為線程名字 |
創建線程第二種方式 | |
Thread(Runnable target) | |
Thread(Runnable target, String name) | name為線程名字 |
6、java語言中,實現線程有兩種方式
第一種方式:
編寫一個類,直接 繼承 java.lang.Thread,重寫 run方法。
- 怎么創建線程對象? new繼承線程的類。
- 怎么啟動線程呢? 調用線程對象的 start() 方法。
偽代碼:
// 定義線程類
public class MyThread extends Thread{public void run(){}
}
// 創建線程對象
MyThread t = new MyThread();
// 啟動線程。
t.start();
eg:
@SpringBootTest
public class Xiexingguo2ApplicationTests {
@Testvoid contextLoads() {MyThread t = new MyThread();// 啟動線程//t.run(); // 不會啟動線程,不會分配新的分支棧。(這種方式就是單線程。)t.start();// 這里的代碼還是運行在主線程中。for (int i = 0; i < 1000; i++) {System.out.println("主線程--->" + i);}}
}
class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("分支線程 = " + i);}}
}
注意:
- t.run() 不會啟動線程,只是普通的調用方法而已。不會分配新的分支棧。(這種方式就是單線程。)
- t.start() 方法的作用是:啟動一個分支線程,在JVM中開辟一個新的棧空間,這段代碼任務完成之后,瞬間就結束了。
這段代碼的任務只是為了開啟一個新的棧空間,只要新的棧空間開出來,start()方法就結束了。線程就啟動成功了。
啟動成功的線程會自動調用run方法,并且run方法在分支棧的棧底部(壓棧)。
run方法在分支棧的棧底部,main方法在主棧的棧底部。run和main是平級的。
調用run()方法內存圖:
調用start()方法內存圖:
第二種方式:
編寫一個類,實現 java.lang.Runnable 接口,實現run方法。
- 怎么創建線程對象? new線程類傳入可運行的類/接口。
- 怎么啟動線程呢? 調用線程對象的 start() 方法。
偽代碼:
// 定義一個可運行的類
public class MyRunnable implements Runnable {public void run(){}
}
// 創建線程對象
Thread t = new Thread(new MyRunnable());
// 啟動線程
t.start();
eg
public class ThreadTest03 {public static void main(String[] args) {Thread t = new Thread(new MyThread());t.start();for (int i = 0; i < 100; i++) {System.out.println("主線程--->" + i);}}
}
public class MyRunnable implements Runnable {public void run() {for (int i = 0; i < 100; i++) {System.out.println("分支線程 = " + i);}}
}
采用匿名內部類創建:
public class ThreadTest04 {public static void main(String[] args) {// 創建線程對象,采用匿名內部類方式。Thread t = new Thread(new Runnable() {public void run() {for (int i = 0; i < 100; i++) {System.out.println("t線程---> " + i);}}});// 啟動線程t.start();for (int i = 0; i < 100; i++) {System.out.println("main線程---> " + i);}}
}
注意:
第二種方式實現接口比較常用,因為一個類實現了接口,它還可以去繼承其它的類,更靈活。
7、獲取當前線程對象、獲取線程對象名字、修改線程對象名字
方法名 | 作用 |
---|---|
static Thread currentThread() | 獲取當前線程對象 |
String getName() | 獲取線程對象名字 |
void setName(String name) | 修改線程對象名字 |
當線程沒有設置名字的時候,默認的名字是什么?
- Thread-0
- Thread-1
- Thread-2
- Thread-3
- …
eg
public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {// currentThread就是當前線程對象。當前線程是誰呢?// 當t1線程執行run方法,那么這個當前線程就是t1// 當t2線程執行run方法,那么這個當前線程就是t2Thread currentThread = Thread.currentThread();System.out.println(currentThread.getName() + "-->" + i);//System.out.println(super.getName() + "-->" + i);//System.out.println(this.getName() + "-->" + i);}}
}
8、關于線程的sleep方法
方法名 | 作用 |
---|---|
static void sleep(long millis) | 讓當前線程休眠millis秒 |
- 靜態方法:Thread.sleep(1000);
- 參數是毫秒
- 作用: 讓當前線程進入休眠,進入“阻塞狀態”,放棄占有CPU時間片,讓給其它線程使用。
這行代碼出現在A線程中,A線程就會進入休眠。
這行代碼出現在B線程中,B線程就會進入休眠。 - Thread.sleep()方法,可以做到這種效果:間隔特定的時間,去執行一段特定的代碼,每隔多久執行一次。
eg
public class ThreadTest05 {public static void main(String[] args) {//每打印一個數字睡1sfor (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);// 睡眠一秒try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
9、關于線程中斷sleep()的方法
方法名 | 作用 |
---|---|
void interrupt() | 終止線程的睡眠 |
eg
public class ThreadTest06 {public static void main(String[] args) {Thread t = new Thread(new MyRunnable2());t.setName("t");t.start();// 希望5秒之后,t線程醒來(5秒之后主線程手里的活兒干完了。)try {Thread.sleep(100*5);} catch (InterruptedException e) {e.printStackTrace();}// 終斷t線程的睡眠(這種終斷睡眠的方式依靠了java的異常處理機制。)t.interrupt();}
}
class MyRunnable2 implements Runnable{public void run() {System.out.println(Thread.currentThread().getName() + "---> begin");// 睡眠1年try {Thread.sleep(1000 * 60 * 60 * 24 * 365);} catch (InterruptedException e