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

Java多線程輪流打印ABC的四種實(shí)現(xiàn)方式

 更新時(shí)間:2025年08月06日 09:41:16   作者:Mylvzi  
在Java中實(shí)現(xiàn)多線程輪流打印ABC的操作是一種常見(jiàn)的并發(fā)編程練習(xí),它展示了如何利用多線程提高程序執(zhí)行的效率和并行性,本文將深入探討如何用Java實(shí)現(xiàn)這一任務(wù),需要的朋友可以參考下

在多線程編程中,一個(gè)經(jīng)典的面試題是:

啟動(dòng)三個(gè)線程,分別打印 A、B、C,要求按順序輪流輸出,如 ABCABCABC… 重復(fù)若干次。

這個(gè)問(wèn)題看似簡(jiǎn)單,但涉及線程間的協(xié)作與通信。本文將從入門(mén)到進(jìn)階,系統(tǒng)講解 4 種主流解法,并附帶完整可運(yùn)行的源碼。

實(shí)現(xiàn)目標(biāo)

假設(shè)每個(gè)線程負(fù)責(zé)打印一個(gè)字符:

  • 線程 A 打印 A
  • 線程 B 打印 B
  • 線程 C 打印 C

期望輸出如下格式:

ABCABCABCABCABC

循環(huán)打印若干次(如 5 次)。

方法一:synchronized + wait/notifyAll

思路解析

使用一個(gè)共享變量 state 表示當(dāng)前輪到哪個(gè)線程。每個(gè)線程進(jìn)入臨界區(qū)后判斷是否是自己該打印的時(shí)機(jī),否則調(diào)用 wait() 掛起,等待喚醒。

示例代碼

/*
 1.設(shè)置共享變量state控制線程的執(zhí)行順序
 2.在打印方法中傳入兩個(gè)參數(shù):打印的內(nèi)容, 當(dāng)前線程的state
 3.每個(gè)線程在調(diào)用打印方法時(shí)都要傳入自己對(duì)應(yīng)的順序,A:0,B:1,C:2
*/
public class PrintABC {
    private static int state = 0;// 設(shè)置共享變量控制線程執(zhí)行順序
    private static final int COUNT = 5;// 控制執(zhí)行順序
    private static Object lock = new Object();// 用于加鎖的對(duì)象

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                printChar("A", 0);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                printChar("B", 1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                printChar("C", 2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }

    private static void printChar(String name, int curState) throws InterruptedException {
        int i = 0;
        while(i < COUNT) {
            synchronized (lock) {
                if(state % 3 == curState) {// 是當(dāng)前線程的執(zhí)行順序
                    System.out.print(name + " ");
                    i++;// 控制當(dāng)前線程的執(zhí)行的次數(shù)
                    state++;// 輪轉(zhuǎn)到下一個(gè)線程
                    lock.notifyAll();
                }else {// 輪不到當(dāng)前線程  wait等待
                    lock.wait();
                }
            }
        }
    }
}

優(yōu)缺點(diǎn)

  • ? 簡(jiǎn)單易懂,容易上手;
  • 使用全局的共享變量state來(lái)控制線程的執(zhí)行順序
  • ? 效率較低,notifyAll() 會(huì)喚醒所有線程。

方法二:ReentrantLock + Condition

思路解析

相比 synchronized,ReentrantLock 提供更靈活的線程調(diào)度機(jī)制,Condition 可以精準(zhǔn)喚醒目標(biāo)線程,避免不必要的喚醒。

示例代碼

    /*
     *第四種方法:ReentrantLock + Condition 來(lái)實(shí)現(xiàn)更加精確的線程間通信
     * ReentrantLock實(shí)現(xiàn)加鎖,解鎖; Condition實(shí)現(xiàn)線程間的通信
     */

    public static final int COUNT = 5;
    public static int state = 0;
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition conA = lock.newCondition();
    public static Condition conB = lock.newCondition();
    public static Condition conC = lock.newCondition();

    public static void main(String[] args) {
        new Thread(() ->printChar("A", 0, conA, conB)).start();
        new Thread(() ->printChar("B", 1, conB, conC)).start();
        new Thread(() ->printChar("C", 2, conC, conA)).start();
    }

    private static void printChar(String name, int curState, Condition curCondition, Condition nextCondition) {
        for(int i = 0; i < COUNT; i++) {
            lock.lock();
                try {
                    while(state % 3 != curState)
                        curCondition.await();

                    // 是當(dāng)前線程  執(zhí)行
                    System.out.print(name);
                    state++;
                    nextCondition.signal();

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    lock.unlock();
                }

        }
    }

優(yōu)缺點(diǎn)

  • ? 支持精準(zhǔn)喚醒,性能優(yōu)于 notifyAll
  • ? 編寫(xiě)稍復(fù)雜,需要手動(dòng)釋放鎖。

方法三:Semaphore 信號(hào)量控制

思路解析

使用 3 個(gè)信號(hào)量 semA、semBsemC 控制線程誰(shuí)可以打印,線程執(zhí)行后釋放下一個(gè)信號(hào)量即可。

示例代碼

    /*
     * 第二種方法:使用Semaphore 信號(hào)量的方式控制執(zhí)行順序
     * 如何保證先打印A:semA, semB, semC  將A的許可設(shè)置為1,B,C的許可設(shè)置為0,則一定先執(zhí)行A
     * 如何保證打印順序:在打印方法中傳入三個(gè)參數(shù):name, curSem, nextSem  A-B  B-C  C-A
     * 執(zhí)行完當(dāng)前線程打印內(nèi)容之后,讓下一個(gè)線程release一個(gè)許可
     */

    private static final Semaphore semA = new Semaphore(1);
    private static final Semaphore semB = new Semaphore(0);
    private static final Semaphore semC = new Semaphore(0);
    private static final int COUNT = 5;

    public static void main(String[] args) {
        new Thread(() -> printChar("A", semA, semB)).start();
        new Thread(() -> printChar("B", semB, semC)).start();
        new Thread(() -> printChar("C", semC, semA)).start();
    }

    private static void printChar(String name, Semaphore cur, Semaphore next) {
        for(int i = 0; i < COUNT;) {
            try {
                cur.acquire();
                System.out.print(name + " ");
                ++i;
                next.release();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

優(yōu)缺點(diǎn)

  • ? 信號(hào)機(jī)制清晰,邏輯明確;
  • ? 不支持靈活的線程增刪;

方法四:BlockingQueue 隊(duì)列傳令

思路解析

為每個(gè)線程分配一個(gè)阻塞隊(duì)列,當(dāng)隊(duì)列有“令牌”時(shí)線程執(zhí)行,執(zhí)行完畢后將令牌交給下一個(gè)隊(duì)列。
阻塞隊(duì)列是一個(gè)線程安全的隊(duì)列

  1. 隊(duì)列為空時(shí),take會(huì)阻塞
  2. 隊(duì)列為滿時(shí),put會(huì)阻塞

示例代碼

    /*
     * 第三種方法:使用BlockingQueue作為令牌的方式來(lái)控制打印順序
     * 創(chuàng)建三個(gè)隊(duì)列,每個(gè)隊(duì)列分別打印對(duì)應(yīng)需要打印的內(nèi)容
     * 使用令牌來(lái)控制打印的順序,和使用semaphore類(lèi)似
     * 在打印的方法中傳入三個(gè)參數(shù):要打印的內(nèi)容,當(dāng)前隊(duì)列,下一個(gè)隊(duì)列
     */

    public static BlockingQueue<String> qA = new ArrayBlockingQueue<>(1);
    public static BlockingQueue<String> qB = new ArrayBlockingQueue<>(1);
    public static BlockingQueue<String> qC = new ArrayBlockingQueue<>(1);
    public static final int COUNT = 5;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> printChar("A", qA, qB)).start();
        new Thread(() -> printChar("B", qB, qC)).start();
        new Thread(() -> printChar("C", qC, qA)).start();

        qA.put("go");
    }

    private static void printChar(String name, BlockingQueue<String> curQueue, BlockingQueue<String> nextQueue) {
        for(int i = 0; i < COUNT; i++) {
            try {
                curQueue.take();// 等待令牌傳遞
                System.out.print(name + " ");
                nextQueue.put("go");// 傳遞令牌
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

優(yōu)缺點(diǎn)

  • ? 隊(duì)列阻塞機(jī)制天然適合線程通信;
  • ? 每個(gè)線程都需獨(dú)立隊(duì)列,稍顯繁瑣。

使用Semaphore和BlockingQueue的方式其實(shí)很像;對(duì)于阻塞隊(duì)列來(lái)說(shuō),是通過(guò)傳遞令牌的方式來(lái)交接接力棒

總結(jié)對(duì)比

方法控制方式喚醒機(jī)制難度推薦場(chǎng)景
synchronized狀態(tài) + 模 3 判斷notifyAll?簡(jiǎn)單測(cè)試、學(xué)習(xí)入門(mén)
ReentrantLock狀態(tài) + Conditionsignal??更精確喚醒,推薦實(shí)際開(kāi)發(fā)使用
Semaphore信號(hào)量控制順序release/acquire??控制有限資源訪問(wèn)/固定順序
BlockingQueue令牌傳遞take/put??結(jié)構(gòu)直觀,適合理解通信流程

寫(xiě)在最后

線程按順序輪流執(zhí)行是實(shí)際開(kāi)發(fā)中很常見(jiàn)的需求,比如:生產(chǎn)者消費(fèi)者模型、有序打印日志、順序處理任務(wù)等。

掌握以上幾種方法不僅能應(yīng)對(duì)面試題,更能提升對(duì) Java 并發(fā)編程的理解。

以上就是Java多線程輪流打印ABC的四種實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于Java多線程輪流打印ABC的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring MVC如何設(shè)置響應(yīng)

    Spring MVC如何設(shè)置響應(yīng)

    本文介紹了如何在Spring框架中設(shè)置響應(yīng),并通過(guò)不同的注解返回靜態(tài)頁(yè)面、HTML片段和JSON數(shù)據(jù),此外,還講解了如何設(shè)置響應(yīng)的狀態(tài)碼和Header
    2025-01-01
  • Java中對(duì)象的創(chuàng)建和銷(xiāo)毀過(guò)程詳析

    Java中對(duì)象的創(chuàng)建和銷(xiāo)毀過(guò)程詳析

    這篇文章主要介紹了Java中對(duì)象的創(chuàng)建和銷(xiāo)毀過(guò)程,對(duì)象的創(chuàng)建過(guò)程包括類(lèi)加載檢查、內(nèi)存分配、初始化零值內(nèi)存、設(shè)置對(duì)象頭和執(zhí)行init方法,對(duì)象的銷(xiāo)毀過(guò)程由垃圾回收機(jī)制負(fù)責(zé),文中介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • Java基礎(chǔ)異常處理代碼及原理解析

    Java基礎(chǔ)異常處理代碼及原理解析

    這篇文章主要介紹了java基礎(chǔ)異常處理代碼及原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Jmeter安裝及配置教程詳解

    Jmeter安裝及配置教程詳解

    很多朋友私信小編Jmeter安裝及配置教程能出一期教程嗎?正巧趕上疫情,不是太忙,下面小編把Jmeter安裝及配置教程分享到腳本之家平臺(tái),感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Maven項(xiàng)目中引入本地JAR包的兩種常見(jiàn)方式

    Maven項(xiàng)目中引入本地JAR包的兩種常見(jiàn)方式

    在開(kāi)發(fā)Java項(xiàng)目時(shí)我們經(jīng)常會(huì)遇到需要引入外部本地jar包的情況,這篇文章主要介紹了Maven項(xiàng)目中引入本地JAR包的兩種常見(jiàn)方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-06-06
  • MyBatis 管理和查找TypeHandler的方法

    MyBatis 管理和查找TypeHandler的方法

    MyBatis通過(guò)TypeHandlerRegistry組件集中管理和查找TypeHandler,TypeHandler用于處理Java類(lèi)型和JDBC類(lèi)型之間的轉(zhuǎn)換,本文介紹MyBatis 管理和查找TypeHandler的方法,感興趣的朋友一起看看吧
    2025-03-03
  • Spring Boot 整合單機(jī)websocket的步驟 附github源碼

    Spring Boot 整合單機(jī)websocket的步驟 附github源碼

    websocket 是一個(gè)通信協(xié)議,通過(guò)單個(gè) TCP 連接提供全雙工通信,這篇文章主要介紹了Spring Boot 整合單機(jī)websocket的步驟(附github源碼),需要的朋友可以參考下
    2021-10-10
  • Jtable和JTree的寫(xiě)法示例代碼

    Jtable和JTree的寫(xiě)法示例代碼

    這篇文章主要介紹了Jtable和JTree的基本概念,常用構(gòu)造方法,以及二者的通用寫(xiě)法,需要的朋友可以了解下。
    2017-09-09
  • nacos客戶(hù)端一致性hash負(fù)載需求實(shí)現(xiàn)

    nacos客戶(hù)端一致性hash負(fù)載需求實(shí)現(xiàn)

    這篇文章主要介紹了nacos客戶(hù)端一致性hash負(fù)載的需求實(shí)現(xiàn)過(guò)程及步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • RabbitMQ消費(fèi)者限流實(shí)現(xiàn)消息處理優(yōu)化

    RabbitMQ消費(fèi)者限流實(shí)現(xiàn)消息處理優(yōu)化

    這篇文章主要介紹了RabbitMQ消費(fèi)者限流實(shí)現(xiàn)消息處理優(yōu)化,消費(fèi)者限流是用于消費(fèi)者每次獲取消息時(shí)限制條數(shù),注意前提是手動(dòng)確認(rèn)模式,并且在手動(dòng)確認(rèn)后才能獲取到消息,感興趣想要詳細(xì)了解可以參考下文
    2023-05-05

最新評(píng)論

双鸭山市| 南安市| 无极县| 乡宁县| 临武县| 桐柏县| 元谋县| 桑日县| 五指山市| 福海县| 金川县| 汝南县| 东乡族自治县| 若尔盖县| 临澧县| 红桥区| 怀集县| 唐山市| 青岛市| 玉山县| 团风县| 青冈县| 根河市| 固阳县| 英吉沙县| 米泉市| 大方县| 玛纳斯县| 遂溪县| 新野县| 海门市| 行唐县| 大化| 澄城县| 中卫市| 拉萨市| 加查县| 肥城市| 怀远县| 瓦房店市| 手游|