成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java 并發(fā)編程基礎(chǔ)小結(jié)

開發(fā)
不同的階段對(duì)于并發(fā)編程的禪修都有不一樣的理解,而本次的進(jìn)階將更多維度是去強(qiáng)調(diào)并發(fā)編程所需要關(guān)注的一些基礎(chǔ)問題和本質(zhì),希望對(duì)你有幫助。

一、并發(fā)編程中的一些核心思想

1. 為什么需要多線程

計(jì)算機(jī)發(fā)展初期都是以進(jìn)程為維度分配內(nèi)存、文件句柄以及安全證書等資源,同時(shí)多個(gè)進(jìn)程之間采用一些比較粗粒度的通信機(jī)制來交換數(shù)據(jù),包括:

  • 套接字
  • 信號(hào)處理器
  • 共享內(nèi)存

基于并發(fā)編程實(shí)戰(zhàn)的思想:

高效做事的人,總能在串行性和異步性之間找到一個(gè)合理的平衡點(diǎn),程序也是如此。

于是操作系統(tǒng)就引入多進(jìn)程運(yùn)行的調(diào)度機(jī)制,例如:在一個(gè)單核的計(jì)算機(jī)上進(jìn)程1得到CPU執(zhí)行權(quán),隨后進(jìn)入IO任務(wù)阻塞掛起,此時(shí)進(jìn)程2、進(jìn)程3先后在此阻塞期間獲得CPU執(zhí)行權(quán)執(zhí)行任務(wù):

基于上述基礎(chǔ)上,考慮到每一個(gè)進(jìn)程都獨(dú)有各自的內(nèi)存空間和文件句柄等資源,以如此龐大級(jí)別的單位處理一些單一的工作而在CPU之間進(jìn)行頻繁切換開銷是非常不客觀的,于是就有了輕量級(jí)調(diào)度單位——多線程。

以多線程調(diào)度為例,假設(shè)進(jìn)程1、進(jìn)程2分別對(duì)應(yīng)讀取定時(shí)讀取網(wǎng)絡(luò)數(shù)據(jù)、定時(shí)寫入數(shù)據(jù)到網(wǎng)絡(luò)系統(tǒng)日志,按照多線程維度將二者合并,最終的進(jìn)程交由CPU執(zhí)行,我們就可以得到這樣一個(gè)場(chǎng)景:

  • CPU執(zhí)行到線程1,讀取網(wǎng)絡(luò)數(shù)據(jù),IO阻塞,讓出CPU。
  • 線程2寫入之前的網(wǎng)絡(luò)系統(tǒng)日志到磁盤,進(jìn)行write調(diào)用時(shí)切換到內(nèi)核態(tài),讓出CPU。
  • 線程1完成數(shù)據(jù),進(jìn)程終端輸出結(jié)果,讓出CPU。
  • 線程2write調(diào)用返回,繼續(xù)進(jìn)行下一次的寫入......

2. 多線程有哪些優(yōu)勢(shì)

如上面所說,多線程存在如下優(yōu)勢(shì):

  • 輕量:以線程為單位構(gòu)成進(jìn)程,共享進(jìn)程范圍內(nèi)的資源,例如內(nèi)存、文件句柄等。
  • 返回多核處理器的強(qiáng)大能力:操作系統(tǒng)以更輕量級(jí)的線程為單位進(jìn)行高效的調(diào)度和切換,在設(shè)計(jì)合理的情況下,可以大大提升CPU的利用率。
  • 建模簡(jiǎn)單性:利用多線程技術(shù),可以將復(fù)雜的異步任務(wù)組合的同步工作流(例如JDK8中的CompleteFuture工具類),并利用多線程分別執(zhí)行這些任務(wù),在指定時(shí)機(jī)進(jìn)行同步交互。
  • 異步事件簡(jiǎn)化處理:有了多線程的概念之后,早期嘗試過用BIO技術(shù)即一個(gè)線程分配一個(gè)客戶端socket,好在現(xiàn)代Unix系統(tǒng)提出epoll、io_uring的良好設(shè)計(jì),使得多線程技術(shù)有了更好的發(fā)揮。

3. 并發(fā)編程需要關(guān)注的問題

(1) 安全性問題

首先是線程安全性問題,因?yàn)槎嗑€程共享了一塊進(jìn)程的數(shù)據(jù),如果沒有充分的做好線程間的同步,就會(huì)出現(xiàn)一些意外的情況,就例如下面這段代碼,多線程操作一個(gè)num,因?yàn)樽栽霾僮鞣菑?fù)合操作且多線程操作彼此不可見,出現(xiàn)意外結(jié)果:

private staticint num = 0;

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(() -> {
            for (int i = 0; i < 100_0000; i++) {
                num++;
            }
            countDownLatch.countDown();
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 100_0000; i++) {
                num++;
            }
            countDownLatch.countDown();
        }).start();

        countDownLatch.await();
        System.out.println(num);//輸出1499633

    }

同樣的,如果沒有良好的同步機(jī)制,編譯器、處理器都可以針對(duì)指令進(jìn)行任意順序和時(shí)間執(zhí)行,同時(shí)在處理器或者寄存器緩存線程變量的情況下的修改操作,其他處理器的線程是無法看到其修改操作,也會(huì)導(dǎo)致邏輯運(yùn)算上的錯(cuò)亂:

(2) 活躍性問題

線程活躍性問題即線程未能按照預(yù)期的時(shí)許執(zhí)行,導(dǎo)致線程持續(xù)的活躍最典型的表現(xiàn)就是無限循環(huán),打滿CPU。例如并發(fā)環(huán)境下兩個(gè)CPU分別執(zhí)行線程0和線程1的邏輯,即:

  • 線程0執(zhí)行無限循環(huán),只要val變?yōu)閠rue則終止無限循環(huán),
  • 線程1休眠一段時(shí)間后將val修改為true。

對(duì)于java并發(fā)編程而言,如果沒有添加保證可見性的關(guān)鍵字進(jìn)行修飾,線程1的修改操作對(duì)于線程0來說是不可見的,此時(shí)就會(huì)出現(xiàn)下圖所示的線程1修改僅對(duì)自己可見,并不會(huì)即使刷新到CPU多核共享內(nèi)存L3 Cache,進(jìn)而導(dǎo)致線程0無限循環(huán),也就是我們所說的活躍性問題:

對(duì)應(yīng)我們也可以出示例代碼,同時(shí)筆者也會(huì)在后續(xù)的文章中來補(bǔ)充說明這一點(diǎn)的解決方案:

private staticboolean val = false;

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(() -> {
            while (!val) {//下方線程操作對(duì)于線程1不可見,進(jìn)行無限循環(huán)

            }
            System.out.println("thread-1 executed finished");
            countDownLatch.countDown();
        }).start();


        new Thread(() -> {
            ThreadUtil.sleep(5, TimeUnit.SECONDS);
            val = true;
            System.out.println("設(shè)置val為true");
            countDownLatch.countDown();

        }).start();

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

通常來說活躍性問題都是由以下幾種錯(cuò)誤導(dǎo)致:

  • 死鎖:即兩個(gè)線程互相等待對(duì)象持有的資源進(jìn)入阻塞
  • 活鎖:上述的活躍性問題就是最經(jīng)典的活鎖
  • 線程饑餓:因?yàn)榫€程過多或者某些原因?qū)е履硞€(gè)線程長(zhǎng)時(shí)間未能分配到CPU時(shí)間片,導(dǎo)致任務(wù)遲遲無法結(jié)束,這就是典型的線程饑餓問題

(3) 性能問題

這一點(diǎn)是老生常態(tài)了,應(yīng)對(duì)并發(fā)安全的手段就是保證可見性和互斥,這涉及CPU緩存更新和臨界資源維度的把控和并發(fā)運(yùn)算技巧,一般來說導(dǎo)致多線程性能瓶頸的幾種原因可分為:

同步機(jī)制抑制了某些編譯器的優(yōu)化,例如synchronized關(guān)鍵字。

共享變量在多處理器之間不同線程執(zhí)行,線程切換時(shí)處理器的緩存數(shù)據(jù)局部性失效,使得開銷大部分時(shí)間都在處理線程調(diào)度而非運(yùn)算,這也會(huì)導(dǎo)致程序的執(zhí)行性能下降。

多線程并發(fā)處理時(shí)切換線程時(shí)產(chǎn)生保存和恢復(fù)上下文的開銷。

二、JVM視角下的進(jìn)程和線程

如下圖所示,可以看出線程是比進(jìn)程更小的單位,進(jìn)程是獨(dú)立的,彼此之間不會(huì)干擾,但是線程在同一個(gè)進(jìn)程中共享堆區(qū)和方法區(qū),雖然開銷較小,但是資源之間管理和分配處理相對(duì)于進(jìn)程之間要更加小心。

三、多線程常見問題

1. 程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧為什么線程中是各自獨(dú)立的

  • 程序計(jì)數(shù)器私有的原因:學(xué)過計(jì)算機(jī)組成原理的小伙伴應(yīng)該都知曉,程序計(jì)數(shù)器用于記錄當(dāng)前下一條要執(zhí)行的指令的單元地址,JVM也一樣,有了程序計(jì)數(shù)器才能保證在多線程的情況下,這個(gè)線程被掛起再被恢復(fù)時(shí),我們可以根據(jù)程序計(jì)數(shù)器找到下一次要執(zhí)行的指令的位置。
  • 虛擬機(jī)棧私有的原因:每一個(gè)Java線程在執(zhí)行方法時(shí),都會(huì)創(chuàng)建一個(gè)棧幀用于保存局部變量、常量池引用、操作數(shù)棧等信息,在這個(gè)方法調(diào)用到完成前,它對(duì)應(yīng)的信息都會(huì)基于棧幀保存在虛擬機(jī)棧上。
  • 本地方法棧私有的原因:和虛擬機(jī)棧類似,只不過本地方法棧保存的native方法的信息。

所以為了保證局部變量不被別的線程訪問到,虛擬機(jī)棧和本地方法棧都是私有的,這就是我們解決某些線程安全問題時(shí),常會(huì)用到一個(gè)叫棧封閉技術(shù)。

關(guān)于棧封閉技術(shù)如下所示,將變量放在局部,每個(gè)線程都有自己的虛擬機(jī)棧,線程安全:

public class StackConfinement implements Runnable {

    //全部變量 多線操作會(huì)有現(xiàn)場(chǎng)問題
    int globalVariable = 0;

    public void inThread() {
        //棧封閉技術(shù),將變量放在局部,每個(gè)線程都有自己的虛擬機(jī)棧 線程安全
        int neverGoOut = 0;
        synchronized (this) {
            for (int i = 0; i < 10000; i++) {
                neverGoOut++;
            }
        }

        System.out.println("棧內(nèi)保護(hù)的數(shù)字是線程安全的:" + neverGoOut);//棧內(nèi)保護(hù)的數(shù)字是線程安全的:10000

    }

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            globalVariable++;
        }
        inThread();
    }

    public static void main(String[] args) throws InterruptedException {
        StackConfinement r1 = new StackConfinement();
        Thread thread1 = new Thread(r1);
        Thread thread2 = new Thread(r1);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        
        System.out.println(r1.globalVariable); //13257
    }
}

2. 并發(fā)和并行的區(qū)別是什么?

  • 并發(fā):并發(fā)我們可以理解為,兩個(gè)線程先后執(zhí)行,但是從宏觀角度來看,他們幾乎是并行的。
  • 并行:并行我們可以理解為兩個(gè)線程同一時(shí)間都在運(yùn)行。

3. 同步和異步是什么意思?

  • 同步:同步就是一個(gè)調(diào)用沒有結(jié)果前,不會(huì)返回,直到有結(jié)果的才返回。
  • 異步:異步即發(fā)起一個(gè)調(diào)用后,不等結(jié)果如何直接返回。

4. 為什么需要多線程,多線程解決了什么問題

從宏觀角度來看:線程可以理解為輕量級(jí)進(jìn)程,切換開銷遠(yuǎn)遠(yuǎn)小于進(jìn)程,所以在多核CPU的計(jì)算機(jī)下,使用多線程可以更好的利用計(jì)算機(jī)資源從而提高計(jì)算機(jī)利用率和效率來應(yīng)對(duì)現(xiàn)如今的高并發(fā)網(wǎng)絡(luò)環(huán)境。

從微觀場(chǎng)景下來說: 單核場(chǎng)景,在單核CPU情況下,假如一個(gè)線程需要進(jìn)行IO才能執(zhí)行業(yè)務(wù)邏輯,若只有單線程,這就意味著IO期間發(fā)生阻塞線程卻只能干等。假如我們使用多線程的話,在當(dāng)前線程IO期間,我們可以將其掛起,讓出CPU時(shí)間片讓其他線程工作。

多核場(chǎng)景下,假如我們有一個(gè)很復(fù)雜的任務(wù)需要進(jìn)程各種IO和業(yè)務(wù)計(jì)算,假如只有一個(gè)線程的話,無論我們有多少個(gè)CPU核心,因?yàn)閱尉€程的緣故他永遠(yuǎn)只能利用一個(gè)CPU核心,假如我們使用多線程,那么這些線程就會(huì)映射到不同的CPU核心上,做到最好的利用計(jì)算機(jī)資源,提高執(zhí)行效率,執(zhí)行事件約為單線程執(zhí)行事件/CPU核心數(shù)。

5. 創(chuàng)建線程方式有哪些

直接繼承Thread啟動(dòng)運(yùn)行:

public static void main(String[] args) {
        new Task().start();
    }

    /**
     * 繼承thread重寫run方法
     */
    private static class Task extends Thread {
        @Override
        public void run() {
            Console.log("{} is running", Thread.currentThread().getName());
        }
    }

通過繼承Runable實(shí)現(xiàn)run方法并提交給thread運(yùn)行:

public static void main(String[] args) {
       new Thread(new Task()).start();
    }

    /**
     * 繼承Runnable重寫run方法
     */
    private static class Task implements Runnable {
        @Override
        public void run() {
            Console.log("{} is running", Thread.currentThread().getName());
        }
    }

6. 為什么需要Runnable接口實(shí)現(xiàn)多線程

由于Java為避免棱形問題所以只支持單繼承,當(dāng)一個(gè)類已有繼承類時(shí),某個(gè)函數(shù)需要實(shí)現(xiàn)異步功能的時(shí)候只能通過接口進(jìn)行拓展,所以才有了Runnable接口。

7. Thread和Runnable使用的區(qū)別

  • 繼承Thread:線程代碼存放在Thread子類的run方法中,調(diào)用start()即可實(shí)現(xiàn)調(diào)用。
  • Runnable:線程代碼存在接口子類的run方法中,需要實(shí)例化一個(gè)線程對(duì)象Thread并將其作為參數(shù)傳入,才能調(diào)用到run方法。

8. Thread類中run()和start()的區(qū)別

  • run:僅僅是方法,在線程實(shí)例化之后使用run等于一個(gè)普通對(duì)象的直接調(diào)用。
  • start:開啟了線程并執(zhí)行線程中的run方法,這期間程序才真正執(zhí)行從用戶態(tài)到內(nèi)核態(tài),創(chuàng)建線程的動(dòng)作。

9. Java線程有哪幾種狀態(tài)

  • 新建(NEW):新創(chuàng)建的了一個(gè)線程對(duì)象,該對(duì)象并沒有調(diào)用start()。
  • 可運(yùn)行(RUNNABLE):線程對(duì)象創(chuàng)建后,并調(diào)用了start方法,等待分配CPU時(shí)間執(zhí)行代碼邏輯。
  • 阻塞(BLOCKED):阻塞狀態(tài),等待鎖的釋放。當(dāng)線程在synchronized 中被wait,然后再被喚醒時(shí),若synchronized 有其他線程在執(zhí)行,那么它就會(huì)進(jìn)入BLOCKED狀態(tài)。
  • 等待(WAITING):因?yàn)槟承┰虮粧炱?,等待其他線程通知或者喚醒。
  • 超時(shí)等待(TIME_WAITING):等待時(shí)間后自行返回,而不像WAITING那樣沒有通知就一直等待。
  • 終止(TERMINATED):該線程執(zhí)行完畢,終止?fàn)顟B(tài)了。
public enum State {
       //線程尚未啟動(dòng)
        NEW,

        //可運(yùn)行的線程狀態(tài),該狀態(tài)代表的是操作系統(tǒng)中線程狀態(tài)的ready或者running狀態(tài)
        RUNNABLE,

        //阻塞等待監(jiān)視器(synchronized底層的monitor lock實(shí)現(xiàn))或者主動(dòng)調(diào)用wait后被喚醒等待獲取監(jiān)視鎖也會(huì)處于該狀態(tài)
        BLOCKED,

       //調(diào)用wait掛起等待notify或者notifyAll
        WAITING,

       //設(shè)置時(shí)限的wait調(diào)用掛起,可能是調(diào)用下面某個(gè)方法
       //Thread.sleep
    //Object.wait with timeout
    //Thread.join with timeout
    //LockSupport.parkNanos
    //LockSupport.parkUntil
        TIMED_WAITING,

        //線程已完成執(zhí)行并終止
        TERMINATED;
    }

10. 和操作系統(tǒng)的線程狀態(tài)的區(qū)別

如下圖所示,實(shí)際上操作系統(tǒng)層面可將RUNNABLE分為Running以及Ready,Java設(shè)計(jì)者之所以沒有區(qū)分那么細(xì)是因?yàn)楝F(xiàn)代計(jì)算機(jī)執(zhí)行效率非常高,這兩個(gè)狀態(tài)在宏觀角度幾乎無法感知?,F(xiàn)代操作系統(tǒng)對(duì)多線程采用時(shí)間分片的搶占式調(diào)度算法,使得每個(gè)線程得到CPU在10-20ms 處于運(yùn)行狀態(tài),然后在讓出CPU時(shí)間片,在不久后又會(huì)被調(diào)度執(zhí)行,所以對(duì)于這種微觀狀態(tài)區(qū)別,Java設(shè)計(jì)者認(rèn)為沒有必要為了這么一瞬間進(jìn)行這么多的狀態(tài)劃分。

11. 什么是上下文切換

線程在執(zhí)行過程中都會(huì)有自己的運(yùn)行條件和狀態(tài),這些運(yùn)行條件和狀態(tài)我們就稱之為線程上下文,這些信息例如程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧等信息。當(dāng)出現(xiàn)以下幾種情況的時(shí)候就會(huì)從占用CPU狀態(tài)中退出:

  • 線程主動(dòng)讓出CPU,例如調(diào)用wait或者sleep等方法。
  • 線程的CPU 時(shí)間片用完 而退出CPU占用狀態(tài) (因?yàn)椴僮飨到y(tǒng)為了避免某些線程獨(dú)占CPU導(dǎo)致其他線程饑餓的情況就設(shè)定的例如時(shí)間分片算法)。
  • 線程調(diào)用了阻塞類型的系統(tǒng)中斷,例如IO請(qǐng)求等。
  • 線程被終止或者結(jié)束運(yùn)行。

上述的前三種情況都會(huì)發(fā)生上下文切換。為了保證線程被切換在恢復(fù)時(shí)能夠繼續(xù)執(zhí)行,所以上下文切換都需要保存線程當(dāng)前執(zhí)行的信息,并恢復(fù)下一個(gè)要執(zhí)行線程的現(xiàn)場(chǎng)。這種操作就會(huì)占用CPU和內(nèi)存資源,頻繁的進(jìn)行上下文切換就會(huì)導(dǎo)致整體效率低下。

12. 線程死鎖問題

如下圖所示,兩個(gè)線程各自持有一把鎖,必須拿到對(duì)方手中那把鎖才能釋放自己的鎖,正是這樣一種雙方僵持的狀態(tài)就會(huì)導(dǎo)致線程死鎖問題。

翻譯稱代碼就如下圖所示:

public class DeadLockDemo {
    publicstaticfinal Object lock1 = new Object();

    publicstaticfinal Object lock2 = new Object();


    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock1){
                System.out.println("線程1獲得鎖1,準(zhǔn)備獲取鎖2");


                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("線程1獲得鎖2");
                }
            }
        }).start();


        new Thread(() -> {
            synchronized (lock2){
                System.out.println("線程2獲得鎖2,準(zhǔn)備獲取鎖1");

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                synchronized (lock1){
                    System.out.println("線程2獲得鎖1");
                }
            }
        }).start();
    }
}

輸出結(jié)果:

線程1獲得鎖1,準(zhǔn)備獲取鎖2
線程2獲得鎖2,準(zhǔn)備獲取鎖1

符合以下4個(gè)條件的場(chǎng)景就會(huì)發(fā)生死鎖問題:

  • 互斥:一個(gè)資源任意時(shí)間只能被一個(gè)線程獲取。
  • 請(qǐng)求與保持條件:一個(gè)線程拿到資源后,在獲取其他資源而進(jìn)入阻塞期間,不會(huì)釋放已有資源。
  • 不可剝奪條件:該資源被線程使用時(shí),其他線程無法剝奪該線程使用權(quán),除非這個(gè)線程主動(dòng)釋放。
  • 循環(huán)等待條件:若干線程獲取資源時(shí),取鎖的流程構(gòu)成一個(gè)頭尾相接的環(huán),如上圖。

預(yù)防死鎖的幾種方式:

  • 破壞請(qǐng)求與保持條件:以上面代碼為例,我們要求所有線程必須一次性獲得兩個(gè)鎖才能進(jìn)行業(yè)務(wù)處理。即要求線程一次性獲得所有資源才能進(jìn)行邏輯處理。
  • 破壞不可剝奪:資源被其他線程獲取時(shí),我們可以強(qiáng)行剝奪使用權(quán)。
  • 破壞循環(huán)等待:這個(gè)就比較巧妙了,例如我們上面lock1 id為1,lock2id為2,我們讓每個(gè)線程取鎖時(shí)都按照lock的id順序取鎖,這樣就避免構(gòu)成循環(huán)隊(duì)列。
  • 操作系統(tǒng)思想(銀行家算法):這個(gè)就涉及到操作系統(tǒng)知識(shí)了,大抵的意思是在取鎖之前對(duì)資源分配進(jìn)行評(píng)估,如果在給定資源情況下不能完成業(yè)務(wù)邏輯,那么就避免這個(gè)線程取鎖,感興趣的讀者可以

13. sleep和wait方法區(qū)別

  • sleep不會(huì)釋放鎖,只是單純休眠一會(huì)。而wait則會(huì)釋放鎖。
  • sleep單純讓線程休眠,在給定時(shí)間后就會(huì)蘇醒,而wait若沒有設(shè)定時(shí)間的話,只能通過notify或者notifyAll喚醒。
  • sleep是Thread 的方法,而wait是Object 的方法
  • wait常用于線程之間的通信或者交互,而sleep單純讓線程讓出執(zhí)行權(quán)。

14. 為什么sleep會(huì)定義在Thread

因?yàn)閟leep要做的僅僅是讓線程休眠,所以不涉及任何鎖釋放等邏輯,放在Thread上最合適。

15. 為什么wait會(huì)定義在Object 上

我們都知道使用wait時(shí)就會(huì)釋放鎖,并讓對(duì)象進(jìn)入WAITING 狀態(tài),會(huì)涉及到資源釋放等問題,所以我們需要將wait放在Object 類上。

16. 可以直接調(diào)用 Thread 類的 run 方法嗎?

若我們編寫run方法,然后調(diào)用Thread 的start方法,線程就會(huì)從用戶態(tài)轉(zhuǎn)內(nèi)核態(tài)創(chuàng)建線程,并在獲取CPU時(shí)間片的時(shí)候開始運(yùn)行,然后運(yùn)行run方法。 若直接調(diào)用run方法,那么該方法和普通方法沒有任何差別,它僅僅是一個(gè)名字為run的普通方法。

17. 假如在進(jìn)程中, 已經(jīng)開辟了多個(gè)線程,  其中一個(gè)線程怎么中斷其它線程?

找到線程對(duì)應(yīng)線程組并基于線程id即可定位到線程,然后調(diào)用interrupt將其打斷即可:

public static Thread getThreadById(long threadId) {
        //獲取線程對(duì)應(yīng)線程組
        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
        //比對(duì)id定位線程
        if (threadGroup != null) {
            Thread[] threads = new Thread[(int) (threadGroup.activeCount() * 1.2)];
            //獲取線程組中獲取的線程數(shù)
            int count = threadGroup.enumerate(threads, true);
            for (int i = 0; i < count; i++) {
                if (threads[i].getId() == threadId) {
                    return threads[i];
                }
            }
        }

        thrownew RuntimeException("未找到線程");
    }

對(duì)應(yīng)的我們也給出使用示例,感興趣的讀者可自行參閱注釋了解實(shí)現(xiàn)細(xì)節(jié):

//創(chuàng)建含有2個(gè)線程的線程池
    privatestaticfinal ExecutorService threadPool = Executors.newFixedThreadPool(2);
    //記錄用于打斷的線程id
    privatestatic Long threadId;


    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        //線程1無限休眠,直到被打斷
        threadPool.execute(() -> {
            Console.log("線程池線程啟動(dòng)執(zhí)行,線程id:{}", Thread.currentThread().getId());
            threadId = Thread.currentThread().getId();
            try {
                TimeUnit.DAYS.sleep(1);
            } catch (InterruptedException e) {
                Console.error("當(dāng)前線程被打斷,線程id:{}", Thread.currentThread().getId(), e);
            } finally {
                countDownLatch.countDown();
            }
        });

        //線程2用于打斷線程1
        threadPool.execute(() -> {
            while (true) {
                if (threadId != null) {
                    Console.log("打斷線程,線程id:{}", threadId);
                    getThreadById(threadId).interrupt();
                    countDownLatch.countDown();
                    break;
                }
                ThreadUtil.sleep(5000);
            }
        });

        countDownLatch.await();
        threadPool.shutdownNow();
    }

對(duì)應(yīng)輸出結(jié)果如下,可以看到threadId 非空時(shí),線程2就會(huì)將休眠的線程1打斷:

18. IO阻塞的線程會(huì)占用CPU資源嗎?如何避免線程霸占CPU?

由于該問題的篇幅比較大,筆者專門寫了一篇文章來討論這兩個(gè)問題,感興趣的朋友可以看看:《IO任務(wù)與CPU調(diào)度藝術(shù)

責(zé)任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關(guān)推薦

2021-02-26 13:08:27

Java高并發(fā)AQS

2019-11-07 09:20:29

Java線程操作系統(tǒng)

2021-03-11 00:05:55

Java高并發(fā)編程

2021-03-18 00:14:29

JavaCyclicBarri高并發(fā)

2021-03-04 07:24:24

JavaSemaphore高并發(fā)

2017-09-19 14:53:37

Java并發(fā)編程并發(fā)代碼設(shè)計(jì)

2025-02-17 00:00:25

Java并發(fā)編程

2025-02-19 00:05:18

Java并發(fā)編程

2011-12-29 13:31:15

Java

2014-05-20 16:27:35

JVMScala

2023-07-03 09:59:00

并發(fā)編程并發(fā)容器

2011-06-08 15:21:18

多維數(shù)組

2024-04-29 09:06:46

線程初始化源碼

2018-12-18 14:08:01

Java內(nèi)存volatile

2025-03-26 00:55:00

2011-07-21 10:17:53

java

2025-01-10 07:10:00

2025-02-06 03:14:38

2025-03-20 06:48:55

性能優(yōu)化JDK

2021-05-07 07:52:51

Java并發(fā)編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩精品1区2区3区 爱爱综合网 | 黄网站在线观看 | 国产精品成人一区二区三区 | 欧美国产一区二区 | 欧美 日韩 中文 | 一区二区三区不卡视频 | 国产日韩亚洲欧美 | 国产成人精品午夜视频免费 | 波多野结衣中文字幕一区二区三区 | 99爱国产| 亚洲欧洲成人 | 开操网 | 国产精品不卡一区 | 91久久久www播放日本观看 | 久久久蜜桃| 激情婷婷成人 | 国产成人aⅴ | 91国产在线播放 | 精品久久久久久久久久久久久久 | 日韩一区二区三区在线观看 | 亚洲精品一区二区网址 | 一区二区三区精品视频 | 天天亚洲| 久久福利| 亚洲福利片| 亚洲视频在线看 | 91av视频在线免费观看 | 亚洲成人一区二区 | 奇米视频777 | 久久精品免费观看 | 蜜桃视频在线观看免费视频网站www | 国产精品亚洲一区 | 99re视频在线观看 | 午夜电影福利 | 久久亚洲国产精品日日av夜夜 | 天天草草草 | 久久日韩精品一区二区三区 | 亚洲 欧美 另类 综合 偷拍 | 激情五月婷婷综合 | 97久久精品午夜一区二区 | 黄色亚洲网站 |