1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        《吃透Java》- 并發(fā)何須懼,工具來(lái)相助!

        共 6331字,需瀏覽 13分鐘

         ·

        2021-07-21 08:54

        大家好,我是小菜。一個(gè)希望能夠成為 吹著牛X談架構(gòu) 的男人!如果你也想成為我想成為的人,不然點(diǎn)個(gè)關(guān)注做個(gè)伴,讓小菜不再孤單!

        本文主要介紹 搬磚必備的并發(fā)工具類

        來(lái)都來(lái)了,點(diǎn)個(gè)在看怎么了~!

        微信公眾號(hào)已開(kāi)啟,小菜良記,沒(méi)關(guān)注的同學(xué)們記得關(guān)注哦!

        作為一名躺平的搬磚工程師,在內(nèi)卷時(shí)期,慢條斯理地搬磚可能已經(jīng)離你而去。磚是一種共享資源,現(xiàn)如今每個(gè)搬磚工都想追求質(zhì)量的又要同時(shí)保持高效的搬磚速率,在爭(zhēng)奪的情況下會(huì)不會(huì)出現(xiàn)并發(fā)的情況?你搬過(guò)的磚卻計(jì)算在別人的KPI上,原本只想 躺平,卻沒(méi)想到躺平也要遭受如此不公!原本只需煎一面的咸魚(yú),現(xiàn)在還得把另一面翻過(guò)來(lái)再煎~!

        終于,躺平的搬磚工決定不再躺平,他捏緊了拳頭,牙齒咬得“格格”作響,他的臉像蠟一樣的黃,嘴唇咬得發(fā)白,原本不多的頭發(fā)一顫一顫地,全身都在瑟瑟地發(fā)抖,狠狠的下定了決定:我一定要解決并發(fā)問(wèn)題!讓搬磚行業(yè)正常的運(yùn)行~!

        什么?正常運(yùn)行,那就得解決并發(fā)問(wèn)題!

        好了好了,氣氛對(duì)頭了,這個(gè)時(shí)候小菜緩步登場(chǎng),那么就進(jìn)入主題,解決并發(fā)問(wèn)題你常用的并發(fā)工具類有哪些?

        JDK 的并發(fā)包中已經(jīng)提供了幾個(gè)非常有用的并發(fā)工具類。

        • CountDownLatch
        • CyclicBarrier
        • Semaphore
        • Exchanger

        這幾個(gè)可能有些小伙伴看的眼熟,可能有點(diǎn)生分,看的眼熟卻不會(huì)用和看的生分的也并無(wú)區(qū)別。那接下來(lái)我們通過(guò)簡(jiǎn)單的闡述,就能讓你在平時(shí)的開(kāi)發(fā)中運(yùn)用自如!

        一、 CountDownLatch

        這是個(gè)在平時(shí)開(kāi)發(fā)中出現(xiàn)頻率較高的并發(fā)工具,它是一個(gè) 倒計(jì)數(shù)器。是一個(gè)非常實(shí)用的多線程控制工具類,這個(gè)工具類常常用來(lái)控制線程等待,可以讓一個(gè)線程等待直到計(jì)數(shù)器結(jié)束再開(kāi)始執(zhí)行!

        我們不必一開(kāi)始就深究源碼,先會(huì)用再善用。因此我們簡(jiǎn)單看個(gè)簡(jiǎn)易的例子

        《一個(gè)都不能少》

        小王老師是一個(gè)嚴(yán)格的老師,她上課有些許任性,必須等到所有學(xué)生(10名)都到場(chǎng)后才會(huì)開(kāi)始上課,也就是但凡一個(gè)學(xué)生不在場(chǎng),都不會(huì)開(kāi)課。

        我們要遵循 一個(gè)都不能少 的要求,也就是當(dāng)學(xué)生人數(shù) < 總?cè)藬?shù)的時(shí)候不能執(zhí)行上課的這個(gè)動(dòng)作。那么這個(gè)時(shí)候我們應(yīng)該怎么處理這個(gè)問(wèn)題呢?

        我們課前點(diǎn)名,增加一個(gè) if 判斷,當(dāng)人數(shù)不滿足的情況下,就不會(huì)進(jìn)入到 上課 的動(dòng)作中。這個(gè)可能是一個(gè)慣性思維,大部分同學(xué)都會(huì)這樣操作。那么問(wèn)題來(lái)了,有些學(xué)生可能只是因?yàn)檫t到,錯(cuò)過(guò)了點(diǎn)名的判斷,當(dāng) if 執(zhí)行結(jié)束后就不會(huì)再判斷,那么錯(cuò)過(guò)就是錯(cuò)過(guò),盡管后續(xù)人數(shù)已經(jīng)到齊了,但最終是開(kāi)不了課的!

        想想再改進(jìn)下,如果因?yàn)?if 只判斷一次而造成的問(wèn)題,那我們能不能一直判斷,那就可以用到了while 或者 for 一直循環(huán)判斷。解決思路是正確的,那我們就順藤引出 CountDownLatch 的用法

        代碼不長(zhǎng),但不知道結(jié)果是否如我們所愿:

        我們可以看到,當(dāng)10名學(xué)生都達(dá)到后,小王老師開(kāi)始上課了,但如果我們這是一個(gè)學(xué)生沒(méi)到達(dá)呢?

        當(dāng)達(dá)到人數(shù)未符合預(yù)期,則不能正常上課,目前看已經(jīng)滿足我們的需求了。那我們緊接著模擬一下學(xué)生遲到的場(chǎng)景~

        依然是學(xué)號(hào)為 10 的同學(xué),雖遲但到,課還是可以正常進(jìn)行上的!

        看來(lái) CountDownLatch 真是一個(gè)好工具,簡(jiǎn)簡(jiǎn)單單就幫我們解決了該問(wèn)題!那他怎么解決的呢?

        CountDownLatch 是通過(guò)一個(gè)計(jì)數(shù)器來(lái)實(shí)現(xiàn)的,首先設(shè)置一個(gè)計(jì)數(shù)器的初始值。每當(dāng)完成一個(gè)任務(wù)后,計(jì)數(shù)器的值就會(huì)減1,當(dāng)計(jì)數(shù)器達(dá)到0 時(shí),它表示所有任務(wù)都已經(jīng)完成,然后在閉鎖上等待的線程可以恢復(fù)執(zhí)行任務(wù).

        我們首先可以要看的是 CountDownLatch 的構(gòu)造方法

        public CountDownLatch(int count) {
            if (count < 0throw new IllegalArgumentException("count < 0");
            this.sync = new Sync(count);
        }

        該方法需要初始化一個(gè)計(jì)數(shù)值,并初始化一個(gè) Sync, 我們這個(gè)時(shí)候不妨大膽猜測(cè),CountDownLatch底層便是靠 Sync 實(shí)現(xiàn)的!我們來(lái)看看 Sync 是個(gè)啥玩意?

        可以看到在Sync內(nèi)部維護(hù)著一個(gè)安全變量 state,它的值便是 計(jì)數(shù)器的值。其中有兩個(gè)重要方法:tryAcquireShared(int acquires)tryReleaseShared(int releases)。那這兩個(gè)方法有什么用呢?

        我們可以先回到 CountDownLatch 類中,上面我們已經(jīng)看到該類構(gòu)造函數(shù)的作用,接下來(lái)需要認(rèn)識(shí)其中兩個(gè)重要的方法:countDown()await()。在我們看來(lái),countDown() 方法便是用來(lái)將計(jì)數(shù)值減 1await() 方法是用來(lái)阻塞判斷計(jì)數(shù)值是否為 0?那我們進(jìn)入對(duì)應(yīng)方法看是如何實(shí)現(xiàn)的

        public void countDown() {
            sync.releaseShared(1);
        }
        ---
        public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }

        這兩個(gè)方法調(diào)用的都是 AQS 中的兩個(gè)方法:(我這邊直接貼源碼注釋,仔細(xì)看哦~!)

        countDown()

        await()

        上面便是 CountDownLatch 的實(shí)現(xiàn),那我們不妨想想該工具類在實(shí)時(shí)系統(tǒng)中的使用場(chǎng)景:

        1. 實(shí)現(xiàn)最大的并行性

        當(dāng)我們想要同時(shí)啟動(dòng)多個(gè)線程,實(shí)現(xiàn)最大程度的并行性。例如,我們想測(cè)試一個(gè)單例類,如果我們創(chuàng)建一個(gè)初始值為 1 的CountDownLatch,并讓所有線程都在這個(gè)鎖上等待,那么我們就可以很輕松的完成測(cè)試,只需要調(diào)用一次 **countDown()**方法就可以讓所有等待線程同時(shí)恢復(fù)執(zhí)行

        1. 開(kāi)始執(zhí)行前等待 n 個(gè)線程完成各自的任務(wù)

        當(dāng)我們應(yīng)用程序執(zhí)行前,確保某些前置動(dòng)作需要執(zhí)行

        1. 死鎖檢測(cè)

        我們可以使用 n 個(gè)線程訪問(wèn)共享資源,在每次測(cè)試階段的線程數(shù)目是不同的,這樣可以嘗試產(chǎn)生死鎖

        二、CyclicBarrier

        CyclicBarrier是另外一種多線程并發(fā)控制工具。Cyclic 意為循環(huán),也就是說(shuō)這個(gè)計(jì)數(shù)器可以反復(fù)使用,它比CountDownLatch更加強(qiáng)大一點(diǎn),它要做的事情是,讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程達(dá)到屏障時(shí),屏障才會(huì)開(kāi)門(mén),所有被屏障攔截的線程才會(huì)繼續(xù)工作。

        也就是說(shuō) CyclicBarrier 是加法計(jì)時(shí)器,我們一樣通過(guò)以上 《一個(gè)都不能少》 例子來(lái)示例如何使用

        這里就不再演示缺課與遲到的示范,與上述 CountDownLatch 實(shí)現(xiàn)方式一致

        這里我們依然關(guān)注兩個(gè)方法,一個(gè)是構(gòu)造方法,一個(gè)是 await()

        我們依然進(jìn)入到 CyclicBarrier 類中查看構(gòu)造方法

        public CyclicBarrier(int parties, Runnable barrierAction) {
            if (parties <= 0throw new IllegalArgumentException();
            this.parties = parties;
            this.count = parties;
            this.barrierCommand = barrierAction;
        }

        可以發(fā)現(xiàn)和上面說(shuō)到的 CountDownLatch 還是有出入的,該構(gòu)造方法只是做了屏障點(diǎn)的記錄,我們重點(diǎn)還是要看 await() 方法

        public int await() throws InterruptedException, BrokenBarrierException {
            try {
                return dowait(false0L);
            } catch (TimeoutException toe) {
                throw new Error(toe); // cannot happen
            }
        }

        追根朔底我們得看 dowait() 方法,進(jìn)入方法可以發(fā)現(xiàn)實(shí)現(xiàn)方式并不復(fù)雜。由于代碼有點(diǎn)長(zhǎng),我們截取重點(diǎn)說(shuō)明

        CountDownLatch 不同的是,屏障點(diǎn)變量并沒(méi)有使用 volatile 修飾,那么毋庸就得加鎖使之線程安全!

        以上便是 CyclicBarrier 的整個(gè)實(shí)現(xiàn)過(guò)程,具體咱就不摳細(xì)節(jié)了~!

        CyclicBarrierCountDownLatch 還是有點(diǎn)類似的,但是我們要清楚他們之間的區(qū)別:

        1. CountDownLatch: 一個(gè)線程(或多個(gè)),等待另外 N 個(gè)線程完成某件事情之后才會(huì)執(zhí)行
        2. CyclicBarrier: N 個(gè)線程之間相互等待,任何一個(gè)線程完成之前,所有的線程都必須等待

        比較重要的一點(diǎn):CountDownLatch 不可重復(fù)利用,CyclicBarrier 不可重復(fù)利用

        三、Semaphore

        信號(hào)量(Semaphore)是為多線程提供了更為強(qiáng)大的控制方法。從廣義上來(lái)講,信號(hào)量是對(duì)鎖的擴(kuò)展。無(wú)論是內(nèi)部鎖synchronized還是重入鎖ReentrantLock,一次都只允許一個(gè)線程訪問(wèn)一個(gè)資源,而信號(hào)量卻可以指定多個(gè)線程,同時(shí)訪問(wèn)某一個(gè)共享資源。

        我們簡(jiǎn)單看個(gè)簡(jiǎn)易的例子

        《搶車位》

        原本一個(gè)小區(qū)有 5 個(gè)地上停車位已經(jīng)可以很好的滿足業(yè)主的停車需求,但是這兩年車輛數(shù)暴增,幾乎家家一車,車位自然供不應(yīng)求,那只能遵循先到先得的原則!

        然后我們看下執(zhí)行結(jié)果:

        可以看到 5 個(gè)車位是共享資源,只有先到的業(yè)主才能搶到車位,當(dāng)搶到車位的業(yè)主離開(kāi)后,后續(xù)的業(yè)主才能進(jìn)入獲取到車位!

        我們提取出關(guān)注點(diǎn)構(gòu)造方法acquire()、release()

        構(gòu)造方法

        public Semaphore(int permits) {
         sync = new NonfairSync(permits);
        }

        public Semaphore(int permits, boolean fair) {
         sync = fair ? new FairSync(permits) : new NonfairSync(permits);
        }

        是的,Semaphore 有兩個(gè)構(gòu)造方法,區(qū)別在于是否使用公平鎖。然后我們繼續(xù)看 aquire()、release()

        public void acquire() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }

        public void release() {
            sync.releaseShared(1);
        }

        excuse me~? 前面有認(rèn)真看的小伙伴,肯定覺(jué)得眼熟了,這調(diào)用的方法豈不是和上面 CountDownLatch 的一樣?是的,這兩個(gè)并發(fā)工具類,底層都是 調(diào)用 AQS 的線程方法。如果不知道這兩個(gè)方法作用的同學(xué),可以上翻查看,這里不再贅述!

        根據(jù)這個(gè)工具類結(jié)合上述例子,我們可以在流量控制的時(shí)候使用!特別是公共資源有限的應(yīng)用場(chǎng)景,比如數(shù)據(jù)庫(kù)連接,假如有一個(gè)需求要讀取幾萬(wàn)個(gè)文件的數(shù)據(jù),因?yàn)槎际?IO 密集型的任務(wù),我們可以啟動(dòng)幾十個(gè)線程去并發(fā)地讀取,但是我們得經(jīng)過(guò)硬盤(pán)->內(nèi)存->數(shù)據(jù)庫(kù),而如果數(shù)據(jù)庫(kù)的連接數(shù)只有10個(gè),那我們這個(gè)時(shí)候就必須要控制只有 10 個(gè)線程可以同時(shí)獲取數(shù)據(jù)庫(kù)連接保存數(shù)據(jù),這個(gè)時(shí)候就可以使用 Semaphore 來(lái)做流量控制~!

        四、Exchanger

        看到這個(gè)名稱,不知道有多少小伙伴腦子里想的是 這是啥?。實(shí)話說(shuō),這個(gè)工具類出鏡率真不高,用的比較少。Exchanger 是一個(gè)用于線程間協(xié)作的工具類。它可用于線程間的數(shù)據(jù)交換,它提供了一個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù),。這兩個(gè)線程通過(guò) Exchanger 方法交換數(shù)據(jù),如果第一個(gè)線程先執(zhí)行 exchange() 方法, 它會(huì)一直等待第二個(gè)線程也執(zhí)行 exchanger() 方法,當(dāng)兩個(gè)線程都到達(dá)同步點(diǎn)時(shí),這兩個(gè)線程就可以交換數(shù)據(jù),將本線程生產(chǎn)出來(lái)的數(shù)據(jù)傳遞給對(duì)方。

        這里注意的是 兩個(gè)線程,不存在**"三角關(guān)系"**

        在沒(méi)有經(jīng)過(guò) exchange() 時(shí),數(shù)字線程 打印的應(yīng)該是數(shù)字,字母線程打印的應(yīng)該是字母,但是經(jīng)過(guò)了 exchange()結(jié)果就發(fā)生了逆轉(zhuǎn):

        注意: 如果兩個(gè)線程中有一個(gè)沒(méi)有執(zhí)行 exchange() 方法,那么則會(huì)一直等待

        為了避免這種情況的發(fā)生,我們可以在 exchange()中加上超時(shí)時(shí)間!

        那么這個(gè)工具類有什么應(yīng)用場(chǎng)景呢?我們想想如果在一個(gè)線程的執(zhí)行任務(wù)中創(chuàng)建某個(gè)對(duì)象的生產(chǎn)代價(jià)很高,而另外一個(gè)線程任務(wù)也需要消費(fèi)到這個(gè)對(duì)象,那我們就可以借助 Exchanger 來(lái)幫助我們傳輸類對(duì)象。甚至于可以實(shí)現(xiàn) 生產(chǎn)者-消費(fèi)者模式!

        以上便是幾種并發(fā)工具類的使用與應(yīng)用場(chǎng)景,當(dāng)然上面提到的應(yīng)用場(chǎng)景只是一小部分,更多的當(dāng)然需要在開(kāi)發(fā)中繼續(xù)挖掘,做到會(huì)用且善用

        看到最后,搬磚工程師掐滅了手中的煙頭,煙霧彌漫的空氣中傳來(lái)一句經(jīng)久不滅的話語(yǔ):他娘的,沒(méi)想到這年頭搬個(gè)磚都不容易了

        不要空談,不要貪懶,和小菜一起做個(gè)吹著牛X做架構(gòu)的程序猿吧~點(diǎn)個(gè)關(guān)注做個(gè)伴,讓小菜不再孤單。咱們下文見(jiàn)!

        看完不贊,都是壞蛋

        今天的你多努力一點(diǎn),明天的你就能少說(shuō)一句求人的話!

        我是小菜,一個(gè)和你一起變強(qiáng)的男人。 ??

        微信公眾號(hào)已開(kāi)啟,小菜良記,沒(méi)關(guān)注的同學(xué)們記得關(guān)注哦!


        瀏覽 58
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            激情小说色图 | 亚洲无限资源 | 美女扒开腿让男人爽视频 | 色综合777 | 国产精品久久久久久妇女 | 男生把裤子脱了吃我的j男男 | 工地被农民工强我好爽 | 一区二区三区无码在线 | 日韩综合在线播放 | 国产福利不卡 |