亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程面試題之交替輸出問(wèn)題的實(shí)現(xiàn)

 更新時(shí)間:2022年01月24日 10:55:39   作者:小成同學(xué)_  
本文主要介紹了Java多線程面試題之交替輸出問(wèn)題的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

交替輸出問(wèn)題

image-20211015145725097

一定要保證交替輸出,這就涉及到兩個(gè)線程的同步問(wèn)題。

有人可能會(huì)想到,用睡眠時(shí)間差來(lái)實(shí)現(xiàn),但是只要是多線程里面,線程同步玩sleep()函數(shù)的,99.99%都是錯(cuò)的。

這道題其實(shí)有100多種解法。

最簡(jiǎn)單的解法

是這個(gè)問(wèn)題的最優(yōu)解,但其實(shí)不是面試官想聽到的答案

關(guān)鍵函數(shù)

Locksupport.park():阻塞當(dāng)前線程
Locksupport.unpark(""):?jiǎn)拘涯硞€(gè)線程

LockSupport

package com.mashibing.juc.c_026_00_interview.A1B2C3
    
import java.util.concurrent.locks.LockSupport;

public class T02_00_LockSupport {
    
    static Thread t1 = null, t2 = null;
    
    public static void main(String[] args) throws Exception {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();
    
        t1 = new Thread(() -> {

                for (char c : aI) {
                    System.out.print(c);
                    LockSupport.unpark(t2); // 叫醒t2
                    LockSupport.park(); // t1阻塞 當(dāng)前線程阻塞
                }

            }, "t1");

            t2 = new Thread(() -> {

                for (char c : aC) {
                    LockSupport.park(); // t2掛起
                    System.out.print(c);
                    LockSupport.unpark(t1); // 叫醒t1
                }

            }, "t2");

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

在這里插入圖片描述

執(zhí)行程序:

image-20211227162406095

是我們想要的結(jié)果。

面試官想聽到的解法

synchronized wait notify

package com.mashibing.juc.c_026_00_interview.A1B2C3

public class T06_00_sync_wait_notify {
    
    public static void main(String[] args) {

        final Object o = new Object();

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(() -> {
            // 首先創(chuàng)建一把鎖
            synchronized (o) {
                for (char c : aI) {
                    System.out.print(c);
                    try {
                        o.notify(); // 叫醒等待隊(duì)列里面的一個(gè)線程,對(duì)本程序來(lái)說(shuō)就是另一個(gè)線程
                        o.wait(); // 讓出鎖
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify(); // 必須,否則無(wú)法停止程序
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (o) {
                for (char c : aC) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "t2").start();
    }
}

可能有人會(huì)想,代碼中的notify()wait()順序是不是沒什么區(qū)別呢?那你就大錯(cuò)特錯(cuò)了,說(shuō)明你不明白notify()wait()是怎么執(zhí)行的。

這道題其實(shí)是華為面試的填空題,讓你填notify()wait()

image-20211227192836696

如果我們先執(zhí)行wait(),會(huì)先讓自己直接進(jìn)入等待隊(duì)列,自己和另一個(gè)線程都在等待隊(duì)列中等待,兩個(gè)線程大??瞪小??,在那傻等,誰(shuí)也叫不醒對(duì)方,也就是根本執(zhí)行不了notify()。

我們發(fā)現(xiàn),在程序的后面還有一個(gè)notify(),而且還是必須有的,為什么是必須呢?我們將它注釋掉,輸出一下看看

image-20211227192149942

其實(shí)這是一個(gè)小坑。

雖然程序可以正常輸出,但是程序沒有結(jié)束;我們可以根據(jù)動(dòng)圖發(fā)現(xiàn),最后一定是有一個(gè)線程是處在wait()狀態(tài)的,沒有人叫醒它,它就會(huì)永遠(yuǎn)處在等待狀態(tài)中,從而程序無(wú)法結(jié)束,為了避免出現(xiàn)這種情況,我們要在后面加上一個(gè)notify()。

但是還有一個(gè)大坑?。?!

玩過(guò)線程的應(yīng)該早就發(fā)現(xiàn)了這個(gè)問(wèn)題,如果第二個(gè)線程先搶到了,那么輸出的就是A1B2C3了,怎么保證第一個(gè)永遠(yuǎn)先輸出的是數(shù)字?

我們可以使用CountDownLatch這個(gè)類,它是JUC新的同步工具,這個(gè)類可以想象成一個(gè)門栓,當(dāng)我們有線程執(zhí)行到門這里,它會(huì)等待門栓把門打開,線程才會(huì)執(zhí)行;如果t2搶先一步,那么它會(huì)執(zhí)行await()方法,因?yàn)橛虚T栓的存在,它只能在門外等待,所以t1線程會(huì)直接執(zhí)行,執(zhí)行到countDown()方法,使創(chuàng)建的CountDownLatch(1)參數(shù)置為0,即釋放門栓,所以永遠(yuǎn)都是t1線程執(zhí)行完,t2線程才會(huì)執(zhí)行。

完整代碼

package com.mashibing.juc.c_026_00_interview.A1B2C3

import java.util.concurrent.CountDownLatch;

public class T07_00_sync_wait_notify {

    private static CountDownLatch latch = new CountDownLatch(1); // 設(shè)置門栓的參數(shù)為1,即只有一個(gè)門栓

    public static void main(String[] args) {

        final Object o = new Object();

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(() -> {
            synchronized (o) {
                for (char c : aI) {
                    System.out.print(c);
                    latch.countDown(); // 門栓的數(shù)值-1,即打開門
                    try {
                        o.notify();
                        o.wait(); 
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify(); 
            }
        }, "t1").start();
        
        new Thread(() -> {
            try {
                latch.await(); // 想哪個(gè)線程后執(zhí)行,await()就放在哪個(gè)線程里
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o) {
                for (char c : aC) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "t2").start();
    }
}

這樣就解決了我們的擔(dān)憂。

更靈活,更精細(xì)的解法

JDK提供了很多新的同步工具,在JUC包下,其中有一個(gè)專門替代synchronized的鎖:Lock。

Lock ReentrantLock await signal

package com.mashibing.juc.c_026_00_interview.A1B2C3
    
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T08_00_lock_condition {
    
        public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

		Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(() -> {
            
            lock.lock();
            
            try {
                for (char c : aI) {
                    System.out.print(c);
                    condition.signal();  // notify()
                    condition.await(); // wait()
                }
                condition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1").start();

        new Thread(() -> {
            lock.lock(); // synchronized
            try {
                for (char c : aC) {
                    System.out.print(c);
                    condition.signal(); // o.notify
                    condition.await(); // o.wait
                }
                condition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }
}

代碼表面看起來(lái),創(chuàng)建鎖,調(diào)用方法跟synchronized沒有區(qū)別,但是關(guān)鍵點(diǎn)在于Condition這個(gè)類,大家應(yīng)該知道生產(chǎn)者消費(fèi)者這個(gè)概念,生產(chǎn)者生產(chǎn)饅頭,生產(chǎn)滿了進(jìn)入等待隊(duì)列,消費(fèi)者吃饅頭,吃光了同樣進(jìn)入等待隊(duì)列,如果我們使用傳統(tǒng)的synchronized,當(dāng)生產(chǎn)者生產(chǎn)滿時(shí),需要從等待隊(duì)列中叫醒消費(fèi)者,但調(diào)用notify方法時(shí),我們能保證一定叫醒的是消費(fèi)者嗎?不能,這件事是無(wú)法做到的,那該怎么保證叫醒的一定是消費(fèi)者呢?

有兩種解決方案:

① 如果籃子已經(jīng)滿了,生產(chǎn)者會(huì)去等待隊(duì)列中叫醒一個(gè)線程,但如果叫醒的線程還是一個(gè)生產(chǎn)者,那么新的生產(chǎn)者起來(lái)之后一定要先檢查一下籃子是否滿了,不能上來(lái)就生產(chǎn),如果是滿的,那接著去叫醒下一個(gè)線程,這樣依次重復(fù),我們一定會(huì)有一次叫醒的是消費(fèi)者。

notifyAll()方法:將等待隊(duì)列中的生產(chǎn)者和消費(fèi)者全喚醒,消費(fèi)者發(fā)現(xiàn)籃子是滿的,就去消費(fèi),生產(chǎn)者發(fā)現(xiàn)籃子是滿的,就繼續(xù)回到等待隊(duì)列。

但不管是這兩個(gè)哪種解決方案,我們喚醒的線程都是不精確的,全都存在著浪費(fèi)。

這就是synchronized做同步的問(wèn)題。

image-20211230191645131

Lock本身就可以解決這個(gè)問(wèn)題,靠的就是Condition,Condition可以做到精確喚醒。

Condition是條件的意思,但我們可以把它當(dāng)做隊(duì)列來(lái)看待。

一個(gè)condition就是一個(gè)等待隊(duì)列。

標(biāo)準(zhǔn)代碼

package com.mashibing.juc.c_026_00_interview.A1B2C3

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

public class T08_00_lock_condition {

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        Condition conditionT1 = lock.newCondition(); // 隊(duì)列1
        Condition conditionT2 = lock.newCondition(); // 隊(duì)列2

        CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {

            lock.lock(); // synchronized

            try {
                for (char c : aI) {
                    System.out.print(c);
                    latch.countDown();
                    conditionT2.signal();  // o.notify()
                    conditionT1.await(); // o.wait()
                }
                conditionT2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1").start();

        new Thread(() -> {

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

            lock.lock(); // synchronized

            try {
                for (char c : aC) {
                    System.out.print(c);
                    conditionT1.signal(); // o.notify
                    conditionT2.await(); // o.wait
                }
                conditionT1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }
}

image-20211230194107871

第一個(gè)線程t1先上來(lái)持有鎖,持有鎖之后叫醒第二隊(duì)列的內(nèi)容,然后自己進(jìn)入第一隊(duì)列等待,同理,t2線程叫醒第一隊(duì)列的內(nèi)容,自己進(jìn)入第二隊(duì)列等待,這樣就可以做到精確喚醒

到此這篇關(guān)于Java多線程面試題之交替輸出問(wèn)題的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java 交替輸出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用線程池實(shí)現(xiàn)socket編程的方法詳解

    Java使用線程池實(shí)現(xiàn)socket編程的方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java使用線程池實(shí)現(xiàn)socket編程的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • 詳解Java如何在業(yè)務(wù)代碼中優(yōu)雅的使用策略模式

    詳解Java如何在業(yè)務(wù)代碼中優(yōu)雅的使用策略模式

    這篇文章主要為大家介紹了Java如何在業(yè)務(wù)代碼中優(yōu)雅的使用策略模式,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解下
    2023-08-08
  • Java集合中contains方法的效率對(duì)比分析

    Java集合中contains方法的效率對(duì)比分析

    這篇文章主要介紹了Java集合中contains方法的效率對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • java判斷各類型字符個(gè)數(shù)實(shí)例代碼

    java判斷各類型字符個(gè)數(shù)實(shí)例代碼

    大家好,本篇文章主要講的是java判斷各類型字符個(gè)數(shù)實(shí)例代碼,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Java基于Calendar類輸出指定年份和月份的日歷代碼實(shí)例

    Java基于Calendar類輸出指定年份和月份的日歷代碼實(shí)例

    這篇文章主要介紹了Java 使用Calendar類輸出指定年份和月份的日歷,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)

    java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)

    下面小編就為大家?guī)?lái)一篇java字符串?dāng)?shù)組進(jìn)行大小排序的簡(jiǎn)單實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-09-09
  • Admin - SpringBoot + Maven 多啟動(dòng)環(huán)境配置實(shí)例詳解

    Admin - SpringBoot + Maven 多啟動(dòng)環(huán)境配置實(shí)例詳解

    這篇文章主要介紹了Admin - SpringBoot + Maven 多啟動(dòng)環(huán)境配置,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Java中對(duì)象都是分配在堆上嗎?你錯(cuò)了!

    Java中對(duì)象都是分配在堆上嗎?你錯(cuò)了!

    這篇文章主要介紹了Java中對(duì)象都是分配在堆上嗎?你錯(cuò)了!文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下
    2019-06-06
  • Java在Excel中創(chuàng)建透視表方法解析

    Java在Excel中創(chuàng)建透視表方法解析

    這篇文章主要介紹了Java在Excel中創(chuàng)建透視表方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • java實(shí)現(xiàn)計(jì)算器模板及源碼

    java實(shí)現(xiàn)計(jì)算器模板及源碼

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)計(jì)算器模板及源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06

最新評(píng)論