【JAVAEE】多線程
- 一、進程
- 1.1 進程的定義
- 1.2 進程和線程的聯系
- 二、線程
- 2.1 JConsole工具
- 2.2 創建線程
- 2.2.1 Thread類,start(),run()
- 2.2.2 繼承Thread類
- 2.2.3 實現Runnable接口
- 2.2.4 匿名內部類
- 2.2.5 使用Runnable接口的匿名內部類
- 2.2.6 使用lambda表達式
- 2.3 Thread類及常用方法
- 2.3.1 Thread類的構造方法
- 2.3.2 Thread類的常見屬性
- 2.4 線程狀態
- 2.5 后臺線程和前臺線程
- 2.6 休眠線程
- 2.7 線程等待
- 2.8 終止線程
- 2.8.1 自定義標志位終止線程
- 2.8.2 使用自帶標志位終止線程
博客結尾包含此篇博客的全部代碼!!!
一、進程
1.1 進程的定義
- 進程(Process)是計算機操作系統中的一個核心概念,它是程序在計算機上的一次動態執行實例。換句話說,進程是程序運行時的活動表現形式。它包含了程序代碼、數據、運行狀態以及系統資源的分配情況。進程相當于一個正在運行的程序。
- 進程也是系統分配資源的基本單位。
1.2 進程和線程的聯系
首先,并發編程成為我們的需求!每個客戶端給服務器發送請求,服務器就需要給客戶端提供服務。在90年代,多進程是一種解決方案!!!
那么既然有解決方法,為什么還要引入線程?
線程:線程是輕量級的進程!!!
線程相比于進程:
- 創建線程比創建進程更快。
- 銷毀線程比銷毀進程更快。
- 調度線程比調度進程更快。
舉個例子:
假設這個任務是“消滅”桌子上的兩只雞,在一個房間中有一個桌子,桌子上放了兩只雞。
進程的解決方法:再創建一個房間,將這兩只雞分開,分別“消滅”。
線程的解決方法:在這個房間中“消滅”這兩只雞。
這兩個對比你發現:相同的人數,進程需要再創建一個房間(開銷很大),而線程則啥都不需要添加。
二、線程
2.1 JConsole工具
在創建線程之前,先給大家介紹一下JConsole 工具, JConsole是JDK 自帶的一款圖形化監控和管理工具,基于 JMX(Java Management Extensions)技術實現。它能夠連接到本地或遠程的 Java 虛擬機(JVM),并實時監控和管理 Java 應用程序的性能和資源使用情況。
這里我們就用它來監控我們的線程!
JConsole:找到我們的JDK安裝的安裝路徑,像我這里安裝的JDK17–>bin–>jconsole.exe
如何使用?
測試代碼:代碼運行起來才能觀察到,如果線程結束就觀察不到。
class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println(" hello Thread");}}
}public class Demo1 {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();}
}
剛進去出現不安全連接,由于是我是自己練習,所以點不安全連接。
2.2 創建線程
2.2.1 Thread類,start(),run()
- Thread類:線程,是操作系統的概念,操作系統定義了一些api(應用程序編程接口)來供程序員使用,而java中將這些api封裝成Thread類來供使用!Thread類在java.long這個包中,所以用的時候不需要導包。
- start():用于啟動一個新線程,線程調度器會調用 run() 方法。start() 方法只能被調用一次。如果嘗試多次調用 start(),會拋出 IllegalThreadStateException 異常。
- run():定義線程的執行邏輯,但不會啟動新線程。如果需要啟動新線程,必須通過 start() 方法。相當于線程的入口。
2.2.2 繼承Thread類
代碼:
class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println(" hello Thread");}}
}public class Demo1 {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();while(true) {System.out.println(" hello main");}}
}
2.2.3 實現Runnable接口
實現Runnable接口的MyRunnnable類,將new MyRunnable()當作一個對象傳給Thread的構造函數。
代碼:
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Hello Thread");}
}public class Demo2 {public static void main(String[] args) {Thread t1 = new Thread(new MyRunnable());t1.start();}
}
2.2.4 匿名內部類
public class Demo3 {public static void main(String[] args) {Thread thread = new Thread(){@Overridepublic void run() {System.out.println("Hello Thread");}};thread.start();System.out.println("Hello main");}
}
2.2.5 使用Runnable接口的匿名內部類
public class Demo4 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("Hello Thread");}});t1.start();}
}
2.2.6 使用lambda表達式
public class Demo5 {public static void main(String[] args) {Thread thread = new Thread(()->{while(true){System.out.println("Hello Thread");}});thread.start();while(true){System.out.println("Hello main");}}
}
2.3 Thread類及常用方法
2.3.1 Thread類的構造方法
解釋一下第四個Thread(Runnable target,String name)!
public class Demo6 {public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("Hello Thread");}}}, "Thread1");thread.start();while (true) {System.out.println("Hello main");}}
}
2.3.2 Thread類的常見屬性
? ID 是線程的唯?標識,不同線程不會重復
? 獲取線程的名稱
? 獲取線程的當前狀態,如新建、就緒、運行、阻塞、等待、終止等
? 優先級?的線程理論上來說更容易被調度到
? 關于后臺線程,需要記住?點:JVM會在?個進程的所有?后臺線程結束后,才會結束運?。
? 是否存活,即簡單的理解,為 run ?法是否運?結束了
? 判斷線程是否被中斷。線程的中斷狀態可以通過調用 interrupt() 方法來設置,該方法會改變線程的中斷狀態,但不會停止線程的執行
2.4 線程狀態
public class Demo7 {public static void main(String[] args) {for(Thread.State state: Thread.State.values()) {System.out.println(state);}}
}
獲取線程的所有狀態:
? NEW:
- 當線程對象被創建,但尚未調用 start() 方法時,線程處于新建狀態。
- 在這個狀態下,線程尚未開始執行。
public class Demo7 {public static void main(String[] args) {Thread thread=new Thread(()->{System.out.println("Hello Thread");});System.out.println(thread.getState());thread.start();}
}
? RUNNABLE:
- 當調用線程的 start() 方法后,線程進入就緒狀態。
- 在這個狀態下,線程已經準備好運行,等待 CPU 時間片以便執行。
- 就緒狀態的線程可能正在 JVM 中運行,也可能正在等待操作系統調度。
public class Demo7 {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(()->{for(int i=0;i<3;i++) {System.out.println("Hello Thread");}});System.out.println(thread.getState());thread.start();System.out.println(thread.getState());}
}
? BLOCKED: 由鎖導致的,后面會出一篇關于線程安全問題,到時候會詳細介紹,這里就不過多介紹了
- 當線程等待獲取一個排他鎖,如同步塊或同步方法中的鎖時,線程進入阻塞狀態。
- 線程在等待鎖釋放后才能繼續執行。
- 阻塞狀態的線程不會被分配 CPU 時間片。
? WAITING:
時間阻塞(這個等待是沒時間上限的)
- 當線程執行 wait()、join() 或 LockSupport.park() 方法后,線程進入等待狀態。
- 在這個狀態下,線程需要等待其他線程執行特定的操作(如通知或中斷)才能繼續。
- 等待狀態的線程不會被分配 CPU 時間片。
public class Demo7 {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(()->{while(true) {}});System.out.println(thread.getState());thread.start();thread.join();}
}
發現main線程是waiting。
? TIMED_WAITING:
時間阻塞(這個等待是有時間上限的),不參與CPU調度
public class Demo7 {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(()->{while(true) {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(thread.getState());thread.start();Thread.sleep(1000);System.out.println(thread.getState());}
}
? TERMINATED:
- 當線程的 run() 方法執行完畢,或者線程被 stop() 方法(已廢棄)強制停止,或者線程拋出未捕獲的異常導致運行結束時,線程進入終止狀態。
- 在這個狀態下,線程已經結束執行,不再消耗任何資源。
public class Demo7 {public static void main(String[] args) throws InterruptedException {Thread thread=new Thread(()->{for(int i=0;i<3;i++) {System.out.println("Hello Thread");}});System.out.println(thread.getState());thread.start();Thread.sleep(6000);System.out.println(thread.getState());}
}
2.5 后臺線程和前臺線程
前臺線程:一般我們創建的線程都是前臺線程,只要前臺線程不結束,JVM就不會退出。
后臺線程:也稱守護線程,后臺線程一般都是輔助性的任務,如果前臺線程全部結束,即使還有后臺線程在運行,JAM也會退出。
public class Demo8 {public static void main(String[] args) {Thread thread1=new Thread(()->{for(int i=0;i<2;i++){System.out.println("Hello Thread");}});thread1.start();System.out.println(thread1.isDaemon());//false 說明他不是守護線程Thread thread2=new Thread(()->{for(int i=0;i<2;i++){System.out.println("Hello Thread");}});thread2.setDaemon(true);thread2.start();System.out.println(thread2.isDaemon());//true}
}
2.6 休眠線程
下面那個可以達到納秒級別,這種適用于軍工,航天等之類,一把情況下用不到。
這里還有一個需要注意的是:由于 Runnable 接口的 run 方法沒有 throws 子句,所以這里只能用try-catch 來捕獲異常。
2.7 線程等待
public class Demo10 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 3; i++){System.out.println("Hello Thread");}});thread.start();thread.join();System.out.println("Hello main");}
}
先執行thread線程,thread線程執行完,再執行main線程。為了防止死等,也可以設置等待時間。
2.8 終止線程
讓run方法盡快結束。
2.8.1 自定義標志位終止線程
public class Demo11 {public static boolean flag=true;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{while(flag){System.out.println("Hello Thread");}System.out.println("Bye Bye Thread");});thread.start();Thread.sleep(1000);flag=false;}
}
將flag設置為全局變量,這里就是匿名內部類訪問外部類,而不是變量捕獲。
為什么不可以將flag設置為局部變量?
很有可能是main線程已經執行完,已經將flag銷毀了,但是Thread線程中還沒執行到while(flag)…
2.8.2 使用自帶標志位終止線程
Java自帶標志位來結束終止線程。先使用Thread.currentThread()來獲取當前線程,在.isInterrupted()獲取標志位。然后再主進程中調用interrupte()方法來將標志位值修改為true。
public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (!Thread.currentThread().interrupted()) {System.out.println("Hello Thread");}System.out.println("Bye Bye Thread");});thread.start();Thread.sleep(1000);thread.interrupt();}
}
調用interrupte()方法,不僅會設置標志位,還會提前喚起sleep阻塞
public class Demo12 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() ->{while (!Thread.currentThread().isInterrupted()) {System.out.println("hahah");try {Thread.sleep(1000);} catch (InterruptedException e) {//1.不操作繼續執行線程(因為sleep喚醒后會又將標志位改為true)e.printStackTrace();//2.結束線程break;//3.進行其它操作}}});thread.start();Thread.sleep(1000);thread.interrupt();}
}
此篇博客的全部代碼!!!