如何建設(shè)好政府門戶網(wǎng)站優(yōu)化網(wǎng)站性能
文章目錄
- Java多線程基礎(chǔ)
- 一、什么是進(jìn)程與線程?
- 二、線程和進(jìn)程的區(qū)別【重點(diǎn)】
- 三、線程的創(chuàng)建方式【重點(diǎn)】
- 1. 繼承Thread類
- 2. 實(shí)現(xiàn)Runnable接口
- 3. lambda 表達(dá)式
- 四、Thread的常見屬性
- 線程中斷
- 自己定義一個標(biāo)志位
- Thread類提供的靜態(tài)方法
- 線程的狀態(tài)
Java多線程基礎(chǔ)
一、什么是進(jìn)程與線程?
進(jìn)程
當(dāng)我們雙擊運(yùn)行電腦程序的時候,操作系統(tǒng)就會為其創(chuàng)建一個進(jìn)程,這個進(jìn)程就是來維護(hù)這個程序在電腦內(nèi)存上運(yùn)行的狀態(tài)(從雙擊運(yùn)行到點(diǎn)擊關(guān)閉期間)線程是操作系統(tǒng)分配資源的基本單位
為什么要有進(jìn)程
進(jìn)程的出現(xiàn)就是因?yàn)槲覀兊膯魏薈PU發(fā)展到了瓶頸了,這時就出現(xiàn)了多核CPU,而進(jìn)程也是為了更加充分的利用多核CPU的資源**(并行+并發(fā))**,但是每個進(jìn)程的創(chuàng)建與銷毀,消耗了太多的資源,所以就以進(jìn)程為基礎(chǔ),剝離出來了線程的概念
并行與并發(fā)
并行:一個CPU以時間片輪轉(zhuǎn)的方式依次執(zhí)行每個線程,某一段時間宏觀來看,就像是多個線程一同執(zhí)行一樣
并發(fā):多個線程在同一個時間點(diǎn)同時運(yùn)行
線程
線程是從進(jìn)程中剝離出來的,因此,一個進(jìn)程是可以剝離出多個線程的,而進(jìn)程是操作系統(tǒng)分配資源的基本單位,所以這多個線程就會公用該進(jìn)程的資源,因此線程的創(chuàng)建與銷毀是比進(jìn)程的消耗更小了,從而提升了并發(fā)編程的效率
雖然線程相對于進(jìn)程的消耗已經(jīng)減少了許多,可是在有的場景下,就是需要頻繁創(chuàng)建與銷毀線程,這時線程的消耗也起來了,所以 Java 進(jìn)入了 線程池的概念
線程是操作系統(tǒng)隨機(jī)調(diào)度的基本單位
主要應(yīng)用場景
- 計算密集型應(yīng)用,為了能在多處理器系統(tǒng)上運(yùn)行,將計算分解到多個線程中實(shí)現(xiàn)
- I/O 密集型,多線程可以充分利用CPU,在執(zhí)行IO操作的時候(需要等待),讓線程去干點(diǎn)別的事情
二、線程和進(jìn)程的區(qū)別【重點(diǎn)】
- 進(jìn)程包含線程,線程是在進(jìn)程內(nèi)部的
- 每個進(jìn)程都有自己獨(dú)立的虛擬地址空間,也有自己獨(dú)立的文件描述符表;同一個進(jìn)程的多個線程之間,則共用這一份虛擬地址空間和文件描述符表
- 進(jìn)程是操作系統(tǒng)中資源分配的基本單位。線程是操作系統(tǒng)中調(diào)度執(zhí)行的基本單位
- 多個進(jìn)程同時運(yùn)行時,如果一個進(jìn)程掛了,一般不會影響別的進(jìn)程;而同一個進(jìn)程里面的多個線程之間,如果一個線程掛了,很可能把整個進(jìn)程帶走了,當(dāng)前進(jìn)程的其他線程也就沒了
三、線程的創(chuàng)建方式【重點(diǎn)】
1. 繼承Thread類
class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("Hello thread");}}
}public class Demo1 {public static void main(String[] args) {Thread thread = new MyThread();thread.start();while (true) {System.out.println("Hello main");}}// 直接匿名內(nèi)部類也可以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. 實(shí)現(xiàn)Runnable接口
class MyRunable implements Runnable {@Overridepublic void run() {System.out.println("hello thread");}
}public class Demo2 {public static void main(String[] args) {Thread thread = new Thread(new MyRunable());thread.start();System.out.println("hello main");}
}
3. lambda 表達(dá)式
public static void main(String[] args) {Thread thread = new Thread(()->{System.out.println("hello thread");});thread.start();System.out.println("hello main");}
四、Thread的常見屬性
屬性 | 獲取方法 |
---|---|
ID | getId() |
名稱 | getName() |
狀態(tài) | getState() |
優(yōu)先級 | getPriority() |
是否有后臺線程 | isDaemon() |
是否存活 | isAlive() |
是否被中斷 | isInterrupted() |
👁?🗨?說明:
ID:是線程的唯一標(biāo)識,多個線程不能重復(fù) (這里能獲取的就是JVM中的ID標(biāo)識,而操作系統(tǒng)內(nèi)部也有一個ID)
名稱:是線程的名稱(方便程序員調(diào)試的時候查看)
狀態(tài):表示線程所處的情況(JVM中的狀態(tài),一共6種,往下看)
優(yōu)先級:理論來說,優(yōu)先級高的線程優(yōu)先被調(diào)度到(它是一個數(shù)值來表示的,數(shù)值越小,優(yōu)先級越高)
后臺線程:JVM會在一個進(jìn)程的所有非后臺線程結(jié)束后,才會結(jié)束運(yùn)行
是否存活:簡單理解為run方法是否運(yùn)行結(jié)束
線程中斷:是否要提前截至 run 方法
線程中斷
兩種中斷機(jī)制:1. 自己定義一個 flag 標(biāo)志位,來控制;2. Thread提供的一個靜態(tài)方法
自己定義一個標(biāo)志位
// 自定義標(biāo)志位來控制線程是否結(jié)束
public class Demo7 {// 用一個布爾變量表示線程是否要結(jié)束private static boolean isQuit = false;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!isQuit) {System.out.println("線程運(yùn)行中……");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("線程執(zhí)行結(jié)束");});t.start();// 5秒鐘后,中斷線程Thread.sleep(5000);isQuit = true;}
}
Thread類提供的靜態(tài)方法
調(diào)用 interrupt() 來實(shí)現(xiàn)中斷,會產(chǎn)生兩種情況:
- 若當(dāng)前線程處于非堵塞狀態(tài),那么程序就會修改內(nèi)置的標(biāo)志位
- 若當(dāng)前線程處于堵塞狀態(tài),inerrupt()的調(diào)用就會讓線程中的sleep拋異常,然后被catch捕獲,然后我們自己決定退不退出
public class Demo8 {public static void main(String[] args) {Thread t = new Thread(() -> {// Thread.currentThread() 獲取當(dāng)前線程的引用while (!Thread.currentThread().isInterrupted()) {System.out.println("線程運(yùn)行中");try {Thread.sleep(1000);} catch (InterruptedException e) {
// e.printStackTrace();// [1] 立即退出
// break;// [2] 稍后退出try {Thread.sleep(1000);} catch (InterruptedException ex) {
// ex.printStackTrace();// 處理退出前的任務(wù)break;}}}System.out.println("線程結(jié)束");});t.start();// 調(diào)用 interrupt() 會產(chǎn)生兩種效果:// 1. 若當(dāng)前線程處于非堵塞狀態(tài),那么程序就會修改內(nèi)置的標(biāo)志位// 2. 若當(dāng)前線程處于堵塞狀態(tài),interrupt()的調(diào)用就會讓線程中的sleep拋異常,然后被catch捕獲,然后我們自己決定退不退出t.interrupt();}
}
線程的狀態(tài)
- NEW:線程創(chuàng)建好了,但是還未執(zhí)行 start 方法,也就是還沒把該線程加到 PCB 隊列中,參與調(diào)度
- TERMINATED:run 方法體執(zhí)行完畢,但是程序還沒結(jié)束(thread 變量還未銷毀)
- RUNNABLE:調(diào)用了 start 方法后的狀態(tài),可能在 CPU上運(yùn)行,也可能在就緒隊列中等待調(diào)度上CPU
- BLOCKED:當(dāng)前線程在等待鎖,導(dǎo)致阻塞
- WAITING:當(dāng)前線程在等待喚醒,導(dǎo)致阻塞(wait 操作)
- TIMED_WARNING:當(dāng)前線程在一定時間內(nèi)阻塞(sleep,join操作)