一個(gè)老掉牙的java面試問題 , 多線程交替打印

一個(gè)老掉牙的java面試問題 , 多線程交替打印。

有打印 ABC 的, 有打印 123 的, 有打印到100的 。

其實(shí)都一樣。

ps: 最近好多小伙伴問這個(gè),這個(gè)題這么熱門么?

實(shí)例實(shí)戰(zhàn)思路:

拿一個(gè)來做示例, 就交替打印ABC. (文末也說下從1到100的)

一起看看這個(gè)小題目 :

主角

三個(gè)線程 線程A 線程 B 線程 C

要做的事

交替打印 A B C

那就是 線程A 負(fù)責(zé)打印 A ,線程 B 負(fù)責(zé)打印 B ,線程C 負(fù)責(zé)打印 C 。

簡(jiǎn)單分析:

A線程打印完, B線程 打印 ;

B線程打印完 ,C 線程打印。

也就是說,這3個(gè)線程 ABC ,有順序/協(xié)作 。

那么這三個(gè)家伙 怎么知道自己要打印東西呢?

那必然是通知等待 。

思路:

三個(gè)線程都 準(zhǔn)備就緒, 準(zhǔn)備大展身手。

接到攜帶暗號(hào)的通知(默認(rèn)暗號(hào)為 A) -> 核對(duì)通知的暗號(hào) -> 打印->然后修改暗號(hào)-> 發(fā)出攜帶暗號(hào)通知(讓別的線程認(rèn)領(lǐng))

如果接到通知,發(fā)現(xiàn)暗號(hào)不對(duì),怎么辦呢? 繼續(xù)進(jìn)入等待,等待下個(gè)通知的到來。

簡(jiǎn)圖:

三個(gè)線程的一舉一動(dòng) :

ps:

  • 如果是線程A打印完,就把通知暗號(hào)改成B,并發(fā)出通知;

  • 如果是線程B打印完,就把通知暗號(hào)改成C,并發(fā)出通知;

  • 同樣,如果是線程C打印完,就把通知改回A,繼續(xù)發(fā)通知。

代碼 :

寫個(gè)ReentrantLock條件控制來搞一下。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class DoTest {

    //控制三個(gè)線程 ABC,保證同一時(shí)刻只有一個(gè)線程工作
    private static Lock lock = new ReentrantLock(true);

    //  Condition ,控制等待或是 通知
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();

    //默認(rèn)的通知 暗號(hào) A
    private static String  CODE= "A";

    public static void main(String[] args) {

        Thread A = new Thread(() -> {
            while (true) {
                try {
                    printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread B = new Thread(() -> {
            while (true) {
                try {
                    printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread C = new Thread(() -> {
            while (true) {
                try {
                    printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        A.start();
        B.start();
        C.start();
    }

    public static void printA() throws InterruptedException {

        //等待
        lock.lock();
        try {
            //核對(duì)
            while (!CODE.equals("A")) {
                //暗號(hào)不對(duì),就進(jìn)入等待
                conditionA.await();
            }
            System.out.println("A");
            //改暗號(hào),通知B
            CODE = "B";
            conditionB.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void printB() throws InterruptedException {
        lock.lock();
        try {
            while (!CODE.equals("B")) {
                conditionB.await();
            }
            System.out.println("B");
            //改暗號(hào),通知C
            CODE = "C";
            conditionC.signalAll();
        } finally {
            lock.unlock();
        }

    }

    public static void printC() throws InterruptedException {
        lock.lock();
        try {
            while (!CODE.equals("C")) {
                conditionC.await();
            }
            System.out.println("C");

            //改暗號(hào),通知A
            CODE = "A";
            conditionA.signalAll();
        } finally {
            lock.unlock();
        }

    }

}

效果:

可以看到,三個(gè)線程 ABC 都開始 無休止的進(jìn)行了 等待 -接通知 -核對(duì)- 打印-改暗號(hào)發(fā)通知 。

當(dāng)然如果需要他們不這么無休止,只需要 做一個(gè)標(biāo)識(shí)進(jìn)行判斷就好,例如 加在一起已經(jīng)打印夠100次了,就停止 之類的限制值。

舉例, 如果交替打印,到100 就停止, 也就是 從1~100 線程A ,線程B ,線程 B 交替打印。

ok,代碼稍作調(diào)整 :

加上2個(gè)值

一個(gè)是打印的數(shù)字,這個(gè)會(huì)一直 +1 輸出;

一個(gè)是用于線程循環(huán)的,之前是while(true) ,這樣會(huì)一直跑。

如果 終止標(biāo)記還是false,就繼續(xù)執(zhí)行:

每個(gè)打印方法都加上判斷和累計(jì)+1的代碼:

看看效果:

整體代碼貼一下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class DoTest {

    //控制三個(gè)線程 ABC,保證同一時(shí)刻只有一個(gè)線程工作
    private static Lock lock = new ReentrantLock(true);

    //  Condition ,控制等待或是 通知
    private static Condition conditionA = lock.newCondition();
    private static Condition conditionB = lock.newCondition();
    private static Condition conditionC = lock.newCondition();

    //默認(rèn)的通知 暗號(hào) A
    private static String CODE = "A";

    //設(shè)置初始打印值
    private static int COUNT_NUM = 1;

    //線程是否需要終止 標(biāo)記
    private static volatile boolean IS_INTERRUPT = false;

    public static void main(String[] args) {

        Thread A = new Thread(() -> {
            while (!IS_INTERRUPT) {
                try {
                    printA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread B = new Thread(() -> {
            while (!IS_INTERRUPT) {
                try {
                    printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread C = new Thread(() -> {
            while (!IS_INTERRUPT) {
                try {
                    printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        A.start();
        B.start();
        C.start();
    }

    public static void printA() throws InterruptedException {

        //等待
        lock.lock();
        try {
            if (COUNT_NUM >= 100) {
                IS_INTERRUPT = true;
                return;
            }
            //核對(duì)
            while (!CODE.equals("A")) {
                //暗號(hào)不對(duì),就進(jìn)入等待
                conditionA.await();
            }
            System.out.println("A, count" + COUNT_NUM);
            //改暗號(hào),通知B
            CODE = "B";
            COUNT_NUM = COUNT_NUM + 1;
            conditionB.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void printB() throws InterruptedException {
        lock.lock();
        try {

            if (COUNT_NUM >= 100) {
                IS_INTERRUPT = true;
                return;
            }

            while (!CODE.equals("B")) {
                conditionB.await();
            }

            System.out.println("B, count" + COUNT_NUM);
            //改暗號(hào),通知C
            CODE = "C";
            COUNT_NUM = COUNT_NUM + 1;

            conditionC.signalAll();
        } finally {
            lock.unlock();
        }

    }

    public static void printC() throws InterruptedException {
        lock.lock();
        try {

            if (COUNT_NUM >= 100) {
                IS_INTERRUPT = true;
                return;
            }

            while (!CODE.equals("C")) {
                conditionC.await();
            }

            System.out.println("C, count" + COUNT_NUM);
            //改暗號(hào),通知A
            CODE = "A";
            COUNT_NUM = COUNT_NUM + 1;
            conditionA.signalAll();
        } finally {
            lock.unlock();
        }

    }

}

可能看到這里,有些初學(xué)者會(huì)心有疑惑,因?yàn)樗麤]了解過 ReentrantLock、Lock 、Condition ,就覺得這個(gè)看了不踏實(shí)。

沒事的,其實(shí)只是為了給大家實(shí)打?qū)嵎治鏊悸罚?用synchronized 配合 wait() 和 notifyAll 也是一樣的。

notifyAll 大不了就全部通知喚醒,然后自己核對(duì)暗號(hào)再進(jìn)入等待。

示例,上代碼:

public class DoTest1 {
    private volatile int COUNT_NUM = 1;
    private volatile String CODE = "A";
    private static int oneTimes = 34;
    private static int othersTimes = 33;

    void onePrint() {
        synchronized (this) {
            while(!CODE.equals("A")) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
            COUNT_NUM++;
            CODE = "B";
            notifyAll();
        }
    }
    void twoPrint() {
        synchronized (this) {
            while(!CODE.equals("B")) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
            COUNT_NUM++;
            CODE = "C";
            notifyAll();
        }
    }
    void threePrint() {
        synchronized (this) {
            while(!CODE.equals("C")) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": " + COUNT_NUM);
            COUNT_NUM++;
            CODE = "A";
            notifyAll();
        }
    }

    public static void main(String[] args) {
        DoTest1 printNumber = new DoTest1();
        new Thread(() -> {
            for (int i = 0; i < oneTimes; i++) {
                printNumber.onePrint();
            }
        },"線程A").start();

        new Thread(() -> {
            for (int i = 0; i < othersTimes; i++) {
                printNumber.twoPrint();
            }
        },"線程B").start();

        new Thread(() -> {
            for (int i = 0; i < othersTimes; i++) {
                printNumber.threePrint();
            }
        },"線程C").start();
    }
}

代碼簡(jiǎn)析:

看看效果:

好了,該篇就到這吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容