最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java線程方法之從線程角色到實戰(zhàn)避坑指南

 更新時間:2025年12月21日 11:43:45   作者:蘿卜飛(音樂人)  
Java語言支持多線程編程,它允許在同一個程序中同時運(yùn)行多個線程來執(zhí)行不同的任務(wù),這有助于提高程序的執(zhí)行效率和響應(yīng)速度,這篇文章主要介紹了Java線程方法之從線程角色到實戰(zhàn)避坑指南的相關(guān)資料,需要的朋友可以參考下

各線程控制方法的典型使用場景(深度詳解)

針對初學(xué)者的理解特點,我會對join()、sleep()、wait()/notify()yield()、LockSupport.park()/unpark()這 5 個核心方法,按照適配場景、通用做法、實戰(zhàn)案例、避坑指南、小總結(jié)的維度逐一拆解,全程明確標(biāo)注「當(dāng)前線程」和「目標(biāo)線程」,所有案例都是可直接運(yùn)行的極簡代碼,保證一看就懂、一跑就通。

一、Thread.join ():等其他線程 “干完活” 再繼續(xù)

1. 適配場景(什么時候用?)

join()的核心是當(dāng)前線程依賴其他線程(目標(biāo)線程)的執(zhí)行結(jié)果,常見場景:

  • 主線程(當(dāng)前線程)需要子線程(目標(biāo)線程)的計算結(jié)果、下載的文件、查詢的數(shù)據(jù)庫數(shù)據(jù)等;
  • 多線程任務(wù)匯總(主線程作為當(dāng)前線程,等待多個子線程(目標(biāo)線程)處理完任務(wù)后統(tǒng)一匯總);
  • 控制線程執(zhí)行順序(線程 A(當(dāng)前線程)等線程 B(目標(biāo)線程)執(zhí)行完后,再執(zhí)行自身邏輯)。

2. 通用做法(標(biāo)準(zhǔn)寫法)

  1. 先創(chuàng)建并啟動目標(biāo)線程(被等待的線程);
  2. 當(dāng)前線程中調(diào)用目標(biāo)線程的join()方法;
  3. 必須處理InterruptedException(捕獲或聲明拋出);
  4. 多線程場景下,當(dāng)前線程逐個調(diào)用目標(biāo)線程的join()或用循環(huán)批量調(diào)用。

3. 實戰(zhàn)案例

案例 1:主線程依賴子線程的計算結(jié)果

明確線程角色

  • 當(dāng)前線程:main 主線程(執(zhí)行calcThread.join()的線程);
  • 目標(biāo)線程:calcThread 子線程(被主線程等待的線程)。
public class JoinDemo1 {
    // 共享變量存儲子線程的計算結(jié)果
    private static int sum = 0;

    public static void main(String[] args) throws InterruptedException {
        // 目標(biāo)線程:計算1-100的和
        Thread calcThread = new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                sum += i;
            }
            System.out.println("目標(biāo)線程(calcThread):計算完成,sum=" + sum);
        });

        // 啟動目標(biāo)線程
        calcThread.start();
        // 當(dāng)前線程(主線程)等待目標(biāo)線程計算完成
        calcThread.join();

        // 當(dāng)前線程(主線程)使用目標(biāo)線程的結(jié)果
        System.out.println("當(dāng)前線程(主線程):拿到結(jié)果,sum=" + sum);
    }
}

輸出結(jié)果

目標(biāo)線程(calcThread):計算完成,sum=5050
當(dāng)前線程(主線程):拿到結(jié)果,sum=5050

案例 2:多線程任務(wù)匯總(3 個線程下載文件,主線程等全部完成)

明確線程角色

  • 當(dāng)前線程:main 主線程(執(zhí)行t1.join()/t2.join()/t3.join()的線程);
  • 目標(biāo)線程:t1、t2、t3 下載線程(被主線程等待的線程)。
public class JoinDemo2 {
    public static void main(String[] args) throws InterruptedException {
        // 創(chuàng)建3個目標(biāo)線程(下載線程)
        Thread t1 = new Thread(() -> System.out.println("目標(biāo)線程(t1):文件1下載完成"), "下載線程1");
        Thread t2 = new Thread(() -> System.out.println("目標(biāo)線程(t2):文件2下載完成"), "下載線程2");
        Thread t3 = new Thread(() -> System.out.println("目標(biāo)線程(t3):文件3下載完成"), "下載線程3");

        // 啟動所有目標(biāo)線程
        t1.start();
        t2.start();
        t3.start();

        // 當(dāng)前線程(主線程)等待所有目標(biāo)線程完成
        t1.join();
        t2.join();
        t3.join();

        // 當(dāng)前線程(主線程)匯總結(jié)果
        System.out.println("當(dāng)前線程(主線程):所有文件下載完成,開始合并文件!");
    }
}

輸出結(jié)果

目標(biāo)線程(t1):文件1下載完成
目標(biāo)線程(t2):文件2下載完成
目標(biāo)線程(t3):文件3下載完成
當(dāng)前線程(主線程):所有文件下載完成,開始合并文件!

4. 避坑指南(初學(xué)者必看)

坑點表現(xiàn)解決方案
忘記處理InterruptedException編譯報錯要么用try-catch捕獲,要么在方法上聲明throws InterruptedException
目標(biāo)線程自身調(diào)用join()(比如 t 中調(diào)用t.join()線程永遠(yuǎn)阻塞(自己等自己完成)絕對避免這種寫法
多實例的join()導(dǎo)致 “偽等待”想讓線程全局互斥,卻用了不同實例的join()保證所有線程使用同一個實例,或用靜態(tài)方法 + 類鎖
依賴join(long millis)的精準(zhǔn)超時實際喚醒時間比指定時間長僅把超時作為 “最大等待時間”,不依賴其做精準(zhǔn)定時

5. 小總結(jié)

join()是 **“等待依賴” 的工具 **,核心記?。?/p>

  • 當(dāng)前線程:調(diào)用join()的線程(主動發(fā)起等待的線程);
  • 目標(biāo)線程:被調(diào)用join()的線程(被等待的線程);
  • 誰調(diào)用join(),誰就等;等的是目標(biāo)線程完成,不是自己暫停。

二、Thread.sleep ():讓當(dāng)前線程 “歇一會兒”

1. 適配場景(什么時候用?)

sleep()的核心是當(dāng)前線程主動暫停指定時間,時間到自動喚醒,無目標(biāo)線程(只是當(dāng)前線程自身行為),常見場景:

  • 模擬延遲(比如驗證碼倒計時、測試時模擬網(wǎng)絡(luò)延遲);
  • 降低 CPU 占用(避免空循環(huán)導(dǎo)致 CPU 100% 占用);
  • 簡單定時(比如每隔 1 秒打印一次日志,非精準(zhǔn)場景)。

2. 通用做法(標(biāo)準(zhǔn)寫法)

  1. 當(dāng)前線程調(diào)用Thread.sleep(),用try-catch包裹(必須處理InterruptedException);
  2. 指定合理的暫停時間(毫秒為單位);
  3. 不要依賴sleep()做精準(zhǔn)定時(系統(tǒng)調(diào)度會有誤差);
  4. 同步塊中使用sleep()時,明確其不釋放鎖的特性。

3. 實戰(zhàn)案例

案例 1:驗證碼倒計時(模擬 60 秒倒計時)

明確線程角色

  • 當(dāng)前線程:main 主線程(執(zhí)行Thread.sleep(1000)的線程);
  • 無目標(biāo)線程(只是主線程自身暫停)。
public class SleepDemo1 {
    public static void main(String[] args) {
        // 驗證碼倒計時60秒
        int count = 60;
        while (count > 0) {
            System.out.println("當(dāng)前線程(主線程):驗證碼倒計時:" + count + "秒");
            try {
                // 當(dāng)前線程(主線程)暫停1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count--;
        }
        System.out.println("當(dāng)前線程(主線程):驗證碼已過期,請重新獲??!");
    }
}

輸出結(jié)果:每秒打印一次倒計時,直到 60 秒結(jié)束。

案例 2:降低 CPU 占用(空循環(huán)加 sleep)

明確線程角色

  • 當(dāng)前線程:monitorThread 監(jiān)控線程(執(zhí)行Thread.sleep(5000)的線程);
  • 無目標(biāo)線程。
public class SleepDemo2 {
    public static void main(String[] args) {
        // 模擬一個持續(xù)運(yùn)行的監(jiān)控線程(當(dāng)前線程)
        Thread monitorThread = new Thread(() -> {
            while (true) {
                // 執(zhí)行監(jiān)控邏輯
                System.out.println("當(dāng)前線程(monitorThread):監(jiān)控系統(tǒng)運(yùn)行中...");
                try {
                    // 當(dāng)前線程(monitorThread)每隔5秒監(jiān)控一次
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 被中斷時退出循環(huán)
                    break;
                }
            }
        });
        monitorThread.start();
    }
}

效果:線程每隔 5 秒執(zhí)行一次,CPU 占用率幾乎為 0(如果不加 sleep,空循環(huán)會讓 CPU 核心占滿)。

4. 避坑指南(初學(xué)者必看)

坑點表現(xiàn)解決方案
認(rèn)為sleep()會釋放鎖同步塊中調(diào)用sleep(),其他線程無法獲取鎖記?。?code>sleep()不釋放任何鎖,要釋放鎖用wait()
sleep()做精準(zhǔn)定時實際執(zhí)行時間比預(yù)期長精準(zhǔn)定時用ScheduledExecutorService,而非sleep()
忽略sleep()的中斷異常線程被中斷后,無法正常退出catch塊中處理中斷(比如退出循環(huán))
睡眠時間設(shè)為 0等同于Thread.yield()(主動讓步),但不推薦要讓步直接用yield(),不要用sleep(0)

5. 小總結(jié)

sleep()是 **“自我暫停” 的工具 **,核心記?。?/p>

  • 當(dāng)前線程:執(zhí)行sleep()的線程(自身暫停的線程);
  • 無目標(biāo)線程(只是自己歇,和其他線程無關(guān));
  • 自己歇,不釋放鎖,時間到必醒;適合簡單延遲和降 CPU,不適合精準(zhǔn)定時和線程協(xié)作。

三、Object.wait ()/notify ()/notifyAll ():線程間的 “對話工具”

1. 適配場景(什么時候用?)

wait()/notify()的核心是線程間的條件協(xié)作,涉及兩類線程:

  • 等待線程(當(dāng)前線程):調(diào)用wait()的線程(因條件不滿足而等待的線程);
  • 喚醒線程(目標(biāo)線程):調(diào)用notify()/notifyAll()的線程(滿足條件后喚醒等待線程的線程)。

常見場景:

  • 生產(chǎn)者消費(fèi)者模式(生產(chǎn)者是喚醒線程,消費(fèi)者是等待線程;反之亦然);
  • 線程間條件等待(線程 A 是等待線程,線程 B 是喚醒線程);
  • 任務(wù)隊列的消費(fèi)(消費(fèi)線程是等待線程,添加任務(wù)的線程是喚醒線程)。

2. 通用做法(標(biāo)準(zhǔn)寫法,記死這個模板?。?/h3>
  1. 必須在同步塊 / 同步方法中調(diào)用(持有對象的鎖);
  2. while循環(huán)檢查條件(防止虛假喚醒);
  3. 等待線程(當(dāng)前線程):synchronized(鎖對象) { while(條件不滿足) { 鎖對象.wait(); } // 執(zhí)行操作 }
  4. 喚醒線程(目標(biāo)線程):synchronized(鎖對象) { // 改變條件 鎖對象.notifyAll(); };
  5. 優(yōu)先用notifyAll()而非notify()(避免喚醒錯線程)。

3. 實戰(zhàn)案例

案例 1:經(jīng)典生產(chǎn)者消費(fèi)者模式(隊列滿則生產(chǎn)者等,隊列空則消費(fèi)者等)

明確線程角色

  • 等待線程(當(dāng)前線程):隊列滿時的生產(chǎn)者線程、隊列空時的消費(fèi)者線程;
  • 喚醒線程(目標(biāo)線程):消費(fèi)后的消費(fèi)者線程(喚醒生產(chǎn)者)、生產(chǎn)后的生產(chǎn)者線程(喚醒消費(fèi)者)。
public class WaitNotifyDemo {
    // 共享隊列(用數(shù)組模擬,容量為3)
    private static final int[] QUEUE = new int[3];
    // 隊列當(dāng)前元素個數(shù)
    private static int count = 0;
    // 鎖對象(所有線程共用同一個鎖)
    private static final Object LOCK = new Object();

    // 生產(chǎn)者線程:生產(chǎn)產(chǎn)品(往隊列加元素)
    static class Producer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (LOCK) {
                    // 隊列滿,當(dāng)前線程(生產(chǎn)者)成為等待線程,等待消費(fèi)者喚醒
                    while (count == QUEUE.length) {
                        try {
                            System.out.println("當(dāng)前線程(生產(chǎn)者):隊列滿,等待消費(fèi)者消費(fèi)...");
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 生產(chǎn)產(chǎn)品
                    QUEUE[count] = (int) (Math.random() * 100);
                    System.out.println("當(dāng)前線程(生產(chǎn)者):生產(chǎn)" + QUEUE[count]);
                    count++;
                    // 當(dāng)前線程(生產(chǎn)者)成為喚醒線程,喚醒等待的消費(fèi)者
                    LOCK.notifyAll();
                }
                // 模擬生產(chǎn)間隔
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 消費(fèi)者線程:消費(fèi)產(chǎn)品(從隊列取元素)
    static class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (LOCK) {
                    // 隊列空,當(dāng)前線程(消費(fèi)者)成為等待線程,等待生產(chǎn)者喚醒
                    while (count == 0) {
                        try {
                            System.out.println("當(dāng)前線程(消費(fèi)者):隊列空,等待生產(chǎn)者生產(chǎn)...");
                            LOCK.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 消費(fèi)產(chǎn)品
                    count--;
                    System.out.println("當(dāng)前線程(消費(fèi)者):消費(fèi)" + QUEUE[count]);
                    // 當(dāng)前線程(消費(fèi)者)成為喚醒線程,喚醒等待的生產(chǎn)者
                    LOCK.notifyAll();
                }
                // 模擬消費(fèi)間隔
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        // 啟動1個生產(chǎn)者、1個消費(fèi)者
        new Producer().start();
        new Consumer().start();
    }
}

輸出結(jié)果

當(dāng)前線程(生產(chǎn)者):生產(chǎn)88
當(dāng)前線程(生產(chǎn)者):生產(chǎn)12
當(dāng)前線程(生產(chǎn)者):生產(chǎn)56
當(dāng)前線程(生產(chǎn)者):隊列滿,等待消費(fèi)者消費(fèi)...
當(dāng)前線程(消費(fèi)者):消費(fèi)56
當(dāng)前線程(生產(chǎn)者):生產(chǎn)34
當(dāng)前線程(消費(fèi)者):消費(fèi)34
當(dāng)前線程(消費(fèi)者):消費(fèi)12
當(dāng)前線程(消費(fèi)者):消費(fèi)88
當(dāng)前線程(消費(fèi)者):隊列空,等待生產(chǎn)者生產(chǎn)...
當(dāng)前線程(生產(chǎn)者):生產(chǎn)77
...

案例 2:線程等待初始化完成(子線程等主線程初始化完畢后執(zhí)行)

明確線程角色

  • 等待線程(當(dāng)前線程):workThread 子線程(執(zhí)行LOCK.wait()的線程);
  • 喚醒線程(目標(biāo)線程):main 主線程(執(zhí)行LOCK.notifyAll()的線程)。
public class WaitNotifyDemo2 {
    // 初始化完成標(biāo)記
    private static boolean initDone = false;
    // 鎖對象
    private static final Object LOCK = new Object();

    public static void main(String[] args) {
        // 等待線程(當(dāng)前線程):workThread子線程,等待初始化完成
        Thread workThread = new Thread(() -> {
            synchronized (LOCK) {
                // 等待初始化完成
                while (!initDone) {
                    try {
                        System.out.println("當(dāng)前線程(workThread):等待初始化...");
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("當(dāng)前線程(workThread):初始化完成,開始工作!");
            }
        });
        workThread.start();

        // 喚醒線程(目標(biāo)線程):主線程,執(zhí)行初始化并喚醒子線程
        try {
            Thread.sleep(2000); // 模擬初始化耗時
            synchronized (LOCK) {
                initDone = true;
                System.out.println("當(dāng)前線程(主線程):初始化完成,喚醒子線程!");
                LOCK.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

當(dāng)前線程(workThread):等待初始化...
當(dāng)前線程(主線程):初始化完成,喚醒子線程!
當(dāng)前線程(workThread):初始化完成,開始工作!

4. 避坑指南(初學(xué)者必看,這部分最容易錯?。?/h3>
坑點表現(xiàn)解決方案
不在同步塊中調(diào)用wait()/notify()拋出IllegalMonitorStateException必須先獲取鎖,再調(diào)用方法
if代替while檢查條件出現(xiàn)虛假喚醒(線程被喚醒后,條件依然不滿足)強(qiáng)制用while循環(huán)檢查條件
notify()代替notifyAll()只喚醒一個線程,若該線程不滿足條件,其他線程永遠(yuǎn)等待優(yōu)先用notifyAll(),除非明確只有一個等待線程
喚醒后忘記修改條件線程被喚醒后,條件依然不滿足,再次等待喚醒前必須修改條件(比如initDone = true
鎖對象不唯一不同線程用不同的鎖對象,無法通信所有線程共用同一個鎖對象

5. 小總結(jié)

wait()/notify()是 **“線程對話” 的工具 **,核心記?。?/p>

  • 等待線程(當(dāng)前線程):調(diào)用wait()的線程(因條件不滿足等待的線程);
  • 喚醒線程(目標(biāo)線程):調(diào)用notifyAll()的線程(滿足條件后喚醒的線程);
  • 在同步塊中用 while 檢查條件,用 notifyAll () 喚醒;是線程協(xié)作的基礎(chǔ),生產(chǎn)者消費(fèi)者模式的核心實現(xiàn)方式。

四、Thread.yield ():讓當(dāng)前線程 “讓個座”

1. 適配場景(什么時候用?)

yield()的核心是當(dāng)前線程主動放棄 CPU 執(zhí)行權(quán),回到就緒態(tài),讓操作系統(tǒng)重新調(diào)度無目標(biāo)線程(只是當(dāng)前線程的讓步行為),常見場景:

  • 非核心任務(wù)讓步(比如后臺統(tǒng)計、日志收集等低優(yōu)先級任務(wù),主動讓 CPU 給高優(yōu)先級的業(yè)務(wù)線程);
  • 提升并發(fā)公平性(防止單個線程長期占用 CPU,導(dǎo)致其他線程饑餓);
  • 測試場景(模擬線程調(diào)度的隨機(jī)性)。

2. 通用做法(標(biāo)準(zhǔn)寫法)

  1. 當(dāng)前線程直接調(diào)用Thread.yield()(無需處理異常);
  2. 不依賴yield()保證執(zhí)行順序(操作系統(tǒng)不一定采納讓步請求);
  3. 僅在非核心任務(wù)中使用,不用于核心業(yè)務(wù)邏輯。

3. 實戰(zhàn)案例

案例:高優(yōu)先級業(yè)務(wù)線程與低優(yōu)先級統(tǒng)計線程(統(tǒng)計線程主動讓步)

明確線程角色

  • 當(dāng)前線程:StatThread 統(tǒng)計線程(執(zhí)行Thread.yield()的線程);
  • 無目標(biāo)線程(只是主動給其他線程讓 CPU)。
public class YieldDemo {
    // 業(yè)務(wù)線程(高優(yōu)先級,處理核心邏輯)
    static class BusinessThread extends Thread {
        public BusinessThread() {
            // 設(shè)置高優(yōu)先級(1-10,默認(rèn)5)
            setPriority(Thread.MAX_PRIORITY);
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                System.out.println("業(yè)務(wù)線程:處理訂單" + i);
            }
        }
    }

    // 統(tǒng)計線程(低優(yōu)先級,后臺統(tǒng)計):當(dāng)前線程,執(zhí)行yield()讓步
    static class StatThread extends Thread {
        public StatThread() {
            // 設(shè)置低優(yōu)先級
            setPriority(Thread.MIN_PRIORITY);
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                // 當(dāng)前線程(StatThread)主動讓步,讓業(yè)務(wù)線程先執(zhí)行
                Thread.yield();
                System.out.println("當(dāng)前線程(StatThread):統(tǒng)計訂單" + i);
            }
        }
    }

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

輸出結(jié)果:業(yè)務(wù)線程的訂單處理會優(yōu)先打印,統(tǒng)計線程的打印會穿插在其中(具體順序由操作系統(tǒng)調(diào)度決定)。

4. 避坑指南(初學(xué)者必看)

坑點表現(xiàn)解決方案
依賴yield()保證執(zhí)行順序線程執(zhí)行順序混亂(操作系統(tǒng)可能忽略讓步請求)不要用yield()控制執(zhí)行順序,用join()wait()/notify()
認(rèn)為yield()會讓線程阻塞線程只是回到就緒態(tài),隨時可能被重新調(diào)度記?。?code>yield()不阻塞,只是 “讓個座”
頻繁調(diào)用yield()降低程序執(zhí)行效率(頻繁調(diào)度線程)僅在非核心任務(wù)中偶爾調(diào)用

5. 小總結(jié)

yield()是 **“主動讓步” 的工具 **,核心記住:

  • 當(dāng)前線程:執(zhí)行yield()的線程(主動讓 CPU 的線程);
  • 無目標(biāo)線程(只是讓 CPU,不針對特定線程);
  • 讓 CPU,不阻塞,操作系統(tǒng)不一定采納;適合提升并發(fā)公平性,不適合控制執(zhí)行順序。

五、LockSupport.park ()/unpark ():靈活的 “線程開關(guān)”

1. 適配場景(什么時候用?)

LockSupportpark()/unpark()涉及兩類線程:

  • 阻塞線程(當(dāng)前線程):調(diào)用park()的線程(被阻塞的線程);
  • 喚醒線程(目標(biāo)線程):調(diào)用unpark(Thread t)的線程(喚醒指定線程的線程),unpark()的參數(shù)就是被喚醒的目標(biāo)線程。

常見場景:

  • 自定義同步工具(比如實現(xiàn)自己的CountDownLatchSemaphore,JUC 工具類底層都用它);
  • 解決wait()/notify()的 “喚醒丟失” 問題(可先unparkpark);
  • 靈活的線程通信(精準(zhǔn)喚醒指定線程)。

2. 通用做法(標(biāo)準(zhǔn)寫法)

  1. 阻塞線程(當(dāng)前線程):調(diào)用LockSupport.park()阻塞自身;
  2. 喚醒線程(目標(biāo)線程):調(diào)用LockSupport.unpark(Thread t)喚醒指定的阻塞線程;
  3. 檢查中斷狀態(tài):park()被中斷后不會拋異常,需用Thread.interrupted()檢查并處理。

3. 實戰(zhàn)案例

案例 1:基本的 park/unpark(主線程喚醒子線程)

明確線程角色

  • 阻塞線程(當(dāng)前線程):workThread 子線程(執(zhí)行LockSupport.park()的線程);
  • 喚醒線程(目標(biāo)線程):main 主線程(執(zhí)行LockSupport.unpark(workThread)的線程);
  • 被喚醒的目標(biāo)線程:workThread(unpark()的參數(shù))。
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo1 {
    public static void main(String[] args) {
        // 阻塞線程(當(dāng)前線程):workThread子線程
        Thread workThread = new Thread(() -> {
            System.out.println("當(dāng)前線程(workThread):開始執(zhí)行,準(zhǔn)備阻塞...");
            // 當(dāng)前線程(workThread)阻塞自身
            LockSupport.park();
            System.out.println("當(dāng)前線程(workThread):被喚醒,繼續(xù)執(zhí)行!");
        }, "工作線程");
        workThread.start();

        // 喚醒線程(主線程):延遲2秒后喚醒目標(biāo)線程(workThread)
        try {
            Thread.sleep(2000);
            System.out.println("當(dāng)前線程(主線程):喚醒目標(biāo)線程(workThread)!");
            LockSupport.unpark(workThread);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

當(dāng)前線程(workThread):開始執(zhí)行,準(zhǔn)備阻塞...
當(dāng)前線程(主線程):喚醒目標(biāo)線程(workThread)!
當(dāng)前線程(workThread):被喚醒,繼續(xù)執(zhí)行!

案例 2:解決 “喚醒丟失” 問題(先 unpark 再 park)

明確線程角色

  • 阻塞線程(當(dāng)前線程):workThread 子線程(執(zhí)行LockSupport.park()的線程);
  • 喚醒線程(目標(biāo)線程):main 主線程(執(zhí)行LockSupport.unpark(workThread)的線程);
  • 被喚醒的目標(biāo)線程:workThread。
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo2 {
    public static void main(String[] args) {
        // 阻塞線程(當(dāng)前線程):workThread子線程
        Thread workThread = new Thread(() -> {
            // 先被unpark,再park(不會阻塞)
            System.out.println("當(dāng)前線程(workThread):準(zhǔn)備阻塞...");
            LockSupport.park();
            System.out.println("當(dāng)前線程(workThread):執(zhí)行完成!");
        });

        // 喚醒線程(主線程):先執(zhí)行unpark,喚醒目標(biāo)線程(workThread)
        LockSupport.unpark(workThread);
        System.out.println("當(dāng)前線程(主線程):先執(zhí)行unpark(),喚醒目標(biāo)線程(workThread)");

        // 啟動阻塞線程
        workThread.start();
    }
}

輸出結(jié)果

當(dāng)前線程(主線程):先執(zhí)行unpark(),喚醒目標(biāo)線程(workThread)
當(dāng)前線程(workThread):準(zhǔn)備阻塞...
當(dāng)前線程(workThread):執(zhí)行完成!

(如果是wait()/notify(),先 notify 再 wait 會導(dǎo)致線程永遠(yuǎn)阻塞,而park()/unpark()不會)

4. 避坑指南(初學(xué)者必看)

坑點表現(xiàn)解決方案
忽略park()的中斷狀態(tài)線程被中斷后,park()返回但不拋異常,線程繼續(xù)執(zhí)行Thread.interrupted()檢查中斷狀態(tài),處理中斷邏輯
認(rèn)為unpark()可以多次生效多次unpark()等同于一次(許可只能用一次)每次park()前確保只有一次unpark()
過度使用park()/unpark()簡單場景下代碼復(fù)雜度高簡單場景用wait()/notify(),復(fù)雜場景(自定義同步工具)再用park()/unpark()

5. 小總結(jié)

LockSupport是 **“底層線程控制工具”**,核心記住:

  • 阻塞線程(當(dāng)前線程):執(zhí)行park()的線程(被阻塞的線程);
  • 喚醒線程:執(zhí)行unpark()的線程;
  • 被喚醒的目標(biāo)線程unpark()的參數(shù)指定的線程;
  • 不依賴對象鎖,可先 unpark 再 park,精準(zhǔn)喚醒指定線程;是 JUC 工具的基礎(chǔ),初學(xué)者先掌握用法,后續(xù)學(xué)并發(fā)框架時會更易理解。

六、所有方法的核心對比表(含線程角色,初學(xué)者收藏)

方法核心作用當(dāng)前線程目標(biāo)線程釋放鎖?喚醒方式
join()等其他線程完成調(diào)用join()的線程被調(diào)用join()的線程釋放目標(biāo)線程對象的鎖目標(biāo)線程完成后自動喚醒
sleep()自我暫停指定時間執(zhí)行sleep()的線程不釋放時間到自動喚醒
wait()條件不滿足時等待調(diào)用wait()的線程調(diào)用notifyAll()的線程釋放鎖對象的鎖其他線程喚醒
notify()喚醒等待的線程調(diào)用notify()的線程被喚醒的等待線程不釋放(仍持有鎖)主動調(diào)用notify()/notifyAll()
yield()主動讓步 CPU執(zhí)行yield()的線程不釋放操作系統(tǒng)重新調(diào)度
park()阻塞自身執(zhí)行park()的線程調(diào)用unpark()的線程不釋放其他線程調(diào)用unpark()或中斷
unpark()喚醒指定線程執(zhí)行unpark()的線程unpark()參數(shù)指定的線程不釋放主動調(diào)用unpark()

七、初學(xué)者終極總結(jié)

  1. 記準(zhǔn)線程角色

    • 誰調(diào)用方法,誰大概率是當(dāng)前線程
    • 方法參數(shù)或被 操作的線程,通常是目標(biāo)線程
    • 無參數(shù)、無被 操作線程的方法(如sleep()yield()),一般無目標(biāo)線程。
  2. 記準(zhǔn)核心用途

    • 等別人結(jié)果用join()(當(dāng)前線程等目標(biāo)線程);
    • 自己歇會兒用sleep()(當(dāng)前線程自我暫停,無目標(biāo)線程);
    • 線程對話用wait()/notify()(當(dāng)前線程等待,目標(biāo)線程喚醒);
    • 主動讓步用yield()(當(dāng)前線程讓 CPU,無目標(biāo)線程);
    • 靈活控制用park()/unpark()(當(dāng)前線程阻塞,目標(biāo)線程精準(zhǔn)喚醒)。
  3. 避坑核心點

    • 所有中斷異常都要處理;
    • wait()必須用 while 檢查條件;
    • 鎖對象必須唯一;
    • 不依賴非精準(zhǔn)的定時 / 調(diào)度。

通過上面的案例和明確的線程角色標(biāo)注,你可以直接復(fù)制代碼運(yùn)行,結(jié)合實際效果理解每個方法的使用場景,這比死記硬背更有效。

總結(jié)

到此這篇關(guān)于Java線程方法之從線程角色到實戰(zhàn)避坑的文章就介紹到這了,更多相關(guān)Java線程角色到實戰(zhàn)避坑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)

    IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版)

    這篇文章主要介紹了IDEA?一直scanning?files?to?index的四種完美解決方法(VIP典藏版),推薦第四種方法,第四種方法摸索研究后得出,親測好用,需要的朋友參考下吧
    2023-10-10
  • SpringBoot操作Jedis案例代碼

    SpringBoot操作Jedis案例代碼

    這篇文章主要介紹了SpringBoot操作Jedis案例代碼,代碼部分包括pom依賴、配置相關(guān)參數(shù)、JedisPool的設(shè)置,代碼簡單易懂對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-08-08
  • 詳解Java8與Runtime.getRuntime().availableProcessors()

    詳解Java8與Runtime.getRuntime().availableProcessors()

    這篇文章主要介紹了詳解Java8與Runtime.getRuntime().availableProcessors(),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 解決mybatis plus報錯Invalid bound statement (not found):問題

    解決mybatis plus報錯Invalid bound statement 

    在使用MyBatis時遇到InvalidBoundStatement異常,常因多個MapperScan配置沖突或者包掃描路徑設(shè)置錯誤,解決方法包括保留一個MapperScan聲明、檢查jar包沖突、確保命名空間和掃描路徑正確,使用@TableId注解指定主鍵
    2024-11-11
  • Java開發(fā)框架spring實現(xiàn)自定義緩存標(biāo)簽

    Java開發(fā)框架spring實現(xiàn)自定義緩存標(biāo)簽

    這篇文章主要介紹了Java開發(fā)框架spring實現(xiàn)自定義緩存標(biāo)簽的詳細(xì)代碼,感興趣的小伙伴們可以參考一下
    2015-12-12
  • Java超詳細(xì)分析泛型與通配符

    Java超詳細(xì)分析泛型與通配符

    本篇文章帶大家認(rèn)識Java語法泛型與通配符,泛型和通配符是一個非常抽象的概念,簡單來說,兩者都可以將類型作為“參數(shù)”進(jìn)行傳遞,不過泛型是在你知道傳入什么類型的時候使用的,而通配符是你不確定傳入什么類型的時候使用,本文將介紹泛型與通配符的使用及兩者間的區(qū)別
    2022-03-03
  • Java使用字節(jié)流復(fù)制文件的方法

    Java使用字節(jié)流復(fù)制文件的方法

    這篇文章主要為大家詳細(xì)介紹了Java使用字節(jié)流復(fù)制文件的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • java數(shù)據(jù)輸出打印流PrintStream和PrintWriter面試精講

    java數(shù)據(jù)輸出打印流PrintStream和PrintWriter面試精講

    這篇文章主要為大家介紹了java數(shù)據(jù)輸出打印流PrintStream和PrintWriter面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • java 中 ChannelHandler的用法詳解

    java 中 ChannelHandler的用法詳解

    這篇文章主要介紹了java 中 ChannelHandler的用法詳解的相關(guān)資料,ChannelHandler處理一個I/O event或者攔截一個I/O操作,需要的朋友可以參考下
    2017-08-08
  • Java反射概念與使用實例代碼

    Java反射概念與使用實例代碼

    JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法,下面這篇文章主要給大家介紹了關(guān)于Java反射概念與使用的相關(guān)資料,需要的朋友可以參考下
    2021-11-11

最新評論

沈丘县| 宁海县| 桂林市| 衡阳市| 青阳县| 平罗县| 通城县| 游戏| 平乐县| 横山县| 额尔古纳市| 马龙县| 武安市| 克东县| 台山市| 锦屏县| 台东市| 曲阳县| 遂川县| 沁阳市| 汨罗市| 浦北县| 麻城市| 雅安市| 巴塘县| 祁连县| 陕西省| 哈密市| 裕民县| 内江市| 新邵县| 房山区| 石嘴山市| 江孜县| 新丰县| 荥阳市| 章丘市| 汪清县| 古蔺县| 广饶县| 达孜县|