<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          并發(fā)與并行

          共 2419字,需瀏覽 5分鐘

           ·

          2020-12-28 03:29

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??南風(fēng)知我不易

          來(lái)源 |? urlify.cn/NjuEbe

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          學(xué)習(xí)并發(fā)編程之初好像就一直對(duì)這個(gè)問題含混不清,在閱讀《Java8實(shí)戰(zhàn)》以及網(wǎng)絡(luò)資源的時(shí)候?qū)@個(gè)問題有了更進(jìn)一步的認(rèn)識(shí),特此梳理一下

          什么是并發(fā)、并行?

          這里引用Java8實(shí)戰(zhàn)中的一張圖片來(lái)加以說(shuō)明

          可能從上圖簡(jiǎn)單來(lái)看,并發(fā)是單處理器核心多任務(wù)的交替執(zhí)行,并行是多任務(wù)多處理器核心的同時(shí)執(zhí)行,由于這個(gè)問題并沒有被蓋棺定論規(guī)范化,導(dǎo)致可能不同的人有不同的理解,我也并不能給出一個(gè)嚴(yán)格意義上準(zhǔn)確的定義,但是我綜合他人的觀點(diǎn)給出的自己的定義如下,并行是并發(fā)的一種表現(xiàn)形式,并發(fā)只強(qiáng)調(diào)兩個(gè)任務(wù)的生命周期存在交集,即對(duì)用上面的任務(wù)1開始到結(jié)束的過(guò)程中,如果任務(wù)2也開始了,那么我們就認(rèn)為任務(wù)1和任務(wù)2是并發(fā)的。但是今天想梳理的并不是嚴(yán)格意義上的區(qū)分這兩個(gè)關(guān)聯(lián)緊密的概念,而是討論這兩者能夠給我們的程序帶來(lái)什么?

          并發(fā)更加側(cè)重于壓榨單個(gè)CPU的性能,降低任務(wù)平均時(shí)延,對(duì)于一串任務(wù)(task1,task2,task3...)高并發(fā)并不能加快這些任務(wù)總體完成的時(shí)間,甚至由于線程切換還會(huì)延長(zhǎng)任務(wù)總體完成的時(shí)間,所以它并不是以提高整體響應(yīng)速率為目的的,而并行它使得多個(gè)任務(wù)(任務(wù)之間不相干,簡(jiǎn)化討論,避免多核之間的一致性要求)可以在多個(gè)處理器核中得到真正的同時(shí)處理,而這個(gè)時(shí)候?qū)τ谝幌盗械牟幌喔扇蝿?wù)來(lái)說(shuō),利用并行計(jì)算,就能大大縮短整體的響應(yīng)時(shí)間

          單線程并發(fā)能夠提高任務(wù)的總體處理速度嘛?

          答案是顯然的,不能,而且由于線程切換帶來(lái)的資源開銷,單線程并發(fā)還會(huì)延長(zhǎng)整個(gè)任務(wù)的處理時(shí)間?

          單線程并發(fā)還有必要嘛?

          有必要,而且非常有必要,首先我們假定有四個(gè)任務(wù)1,2,3,4如下,每個(gè)任務(wù)的執(zhí)行耗時(shí)1個(gè)單位時(shí)間,如果按照單線程串行的執(zhí)行方式,它應(yīng)該是這樣的

          對(duì)于task1來(lái)說(shuō),它還能接收,畢竟執(zhí)行1個(gè)單位時(shí)間它就拿到了它想要的結(jié)果,但是對(duì)于后面的task來(lái)說(shuō)就不滿意了,特別是task4來(lái)說(shuō),執(zhí)行task4的耗時(shí)為1個(gè)單位時(shí)間,但是它需要等4個(gè)單位時(shí)間才能拿到結(jié)果,如果在多線程情況下,它是如何的呢?假設(shè)每個(gè)task都另起了一個(gè)線程,且不考慮操作系統(tǒng)任務(wù)調(diào)度耗時(shí)等等,現(xiàn)在的處理情況是這樣的

          假如理想狀況下,每個(gè)任務(wù)被切割得足夠小,那么最終每個(gè)任務(wù)幾乎是同時(shí)開始同時(shí)結(jié)束,那么每個(gè)task的用時(shí)就是總耗時(shí)的平均值也就是2.5,這下task4總算開心了,它不用等那么久了。

          但是實(shí)際問題中,不可能把任務(wù)無(wú)限切分,操作系統(tǒng)的線程調(diào)度也是耗時(shí)操作,那么上面的結(jié)論就不一定那么可靠了,甚至可能每個(gè)時(shí)間都超過(guò)3了,那還不如串行呢,至少task1和task2爽了,那為什么還需要并發(fā)呢?

          因?yàn)閷?shí)際狀況下,每個(gè)任務(wù)的執(zhí)行速度也不可能完全相等,每個(gè)任務(wù)執(zhí)行的速度有快有慢,我們現(xiàn)在假設(shè)task1執(zhí)行用時(shí)需要1000個(gè)單位時(shí)間,如果在串行情況下,task1后面的所有任務(wù)都會(huì)被task1所拖累,需要等待的時(shí)間為1000加,而此時(shí)的并發(fā)執(zhí)行策略中,雖然由于系統(tǒng)調(diào)度等等開銷,task2,3,4仍然可以以一個(gè)與之前速度相差無(wú)幾的時(shí)間響應(yīng),task1帶來(lái)的惡劣影響也單單只影響到了自己。我們上面的策略也就類似于tomcat對(duì)于請(qǐng)求的處理策略,針對(duì)每個(gè)請(qǐng)求都另起一個(gè)線程(processor)來(lái)處理。

          tomcat都這么厲害了,自己的代碼中還有必要多線程嘛?

          有必要,通常一個(gè)大任務(wù)是由多個(gè)小任務(wù)組合而成,如果按照CPU密集型和I/O密集型來(lái)劃分任務(wù)類型的話,對(duì)于CPU密集型任務(wù)來(lái)說(shuō),無(wú)論我們?cè)僭趺炊嗑€程瘋狂操作也好,在單核處理器中,最終都還是依靠單核來(lái)做運(yùn)算,多線程的線程切換開銷無(wú)疑延長(zhǎng)了整個(gè)任務(wù)的處理時(shí)間,但是在I/O密集型任務(wù)情況下(包括磁盤IO,網(wǎng)絡(luò)IO),假設(shè)你發(fā)起了10個(gè)不同的RPC調(diào)用,無(wú)疑多線程的方式能夠讓你同時(shí)發(fā)起多個(gè)請(qǐng)求,多個(gè)請(qǐng)求同時(shí)等待響應(yīng),否則你就只能按照串行的方式,每個(gè)請(qǐng)求都需要等一個(gè)時(shí)延,然后再處理下一個(gè)請(qǐng)求,這樣的等待無(wú)疑延長(zhǎng)了總體響應(yīng)時(shí)間,降低CPU利用率。其實(shí)這樣的并發(fā)就包含了并行,因?yàn)槟惆l(fā)起的遠(yuǎn)程調(diào)用是遠(yuǎn)方的多個(gè)處理器去幫你處理的,我們所做的只不過(guò)是利用并發(fā)在一個(gè)請(qǐng)求傻等著的過(guò)程中又發(fā)起了另一個(gè)請(qǐng)求罷了

          并行

          并行的好處是顯而易見的,多個(gè)處理器干活肯定是快于一個(gè)人干活的,對(duì)于上面討論的情況,如果在多核心的處理器下,并發(fā)之后可能整個(gè)處理過(guò)程就是并行的,小的任務(wù)可以在多個(gè)處理器核心中同時(shí)運(yùn)行,在這里也不太過(guò)多討論并發(fā)安全的問題,主要討論如何高效并行

          在tomcat中想要并行很簡(jiǎn)單,你并發(fā)就好,如果你有多個(gè)處理器核心它自然會(huì)并行執(zhí)行,可能并不太需要我們對(duì)整個(gè)處理過(guò)程進(jìn)行并行處理,關(guān)注更多的是不同請(qǐng)求之間的并行,但是在一些場(chǎng)景下,可能就需要我們關(guān)注整個(gè)任務(wù)本身的并行,這時(shí)候并行就不那么容易,假設(shè)你要計(jì)算1-1000000000的和,你當(dāng)然可以選擇并發(fā)執(zhí)行,自己分割每個(gè)處理器計(jì)算多少到多少的和,然后自行匯總結(jié)果,就像下面的代碼一樣

          public?class?ConcurrentVsParallel?{
          ????public?static?void?main(String[]?args)?throws?ExecutionException,?InterruptedException?{
          ????????//串行
          ????????long?sum=0;
          ????????long?time1=System.currentTimeMillis();
          ????????for?(long?i?=?1;?i?<=?10000000000L;?i++)?{
          ????????????sum+=i;
          ????????}
          ????????System.out.println("串行計(jì)算結(jié)果為:"+sum);
          ????????System.out.println("串行耗時(shí):"+(System.currentTimeMillis()-time1));
          ????????long?time2=?System.currentTimeMillis();
          ????????long?res?=?concurrentCal(10000000000L);
          ????????System.out.println("計(jì)算結(jié)果為:"+res);
          ????????System.out.println("并行耗時(shí)為:"+(System.currentTimeMillis()-time2));
          ????}

          ????public?static?long?concurrentCal(final?long?n)?throws?ExecutionException,?InterruptedException?{
          ????????//4等分來(lái)處理
          ????????ExecutorService?executor?=?Executors.newFixedThreadPool(4);
          ????????long?quarter=n/4;
          ????????long?allSum=0;
          ????????Future[]?parts?=?new?Future[4];
          ????????for?(int?i?=?0;?i?????????????final?int?temp=i;
          ????????????Future?partSum?=?executor.submit(()?->?{
          ????????????????long?sum?=?0;
          ????????????????for?(long?j?=?temp?*?quarter?+?1;?j?<=?(temp?+?1)?*?quarter;?j++)?{
          ????????????????????sum?+=?j;
          ????????????????}
          ????????????????return?sum;
          ????????????});
          ????????????parts[i]=partSum;
          ????????}
          ????????for?(int?i?=?0;?i?????????????allSum+=(long)parts[i].get();
          ????????}
          ????????return?allSum;
          ????}
          }

          輸出結(jié)果如下:

          串行計(jì)算結(jié)果為:-5340232216128654848
          串行耗時(shí):4617
          計(jì)算結(jié)果為:-5340232216128654848
          并行耗時(shí)為:1847

          上述的代碼能夠?qū)崿F(xiàn)我們既定的目標(biāo),但是存在著可讀性和可拓展性的問題,性能也存在著問題,如果需要對(duì)(2-n)求和呢,很簡(jiǎn)單,給我們的代碼加入一個(gè)start即可,但是如果需要對(duì)(2-n)中所有的偶數(shù)求和呢?豈不是又需要改代碼,更加嚴(yán)重的問題是任務(wù)規(guī)模的劃分是定下來(lái)的,導(dǎo)致任務(wù)劃分的粒度有的時(shí)候并不夠,當(dāng)然你也可以再添加一個(gè)參數(shù)設(shè)置任務(wù)規(guī)模的劃分,但是上述這些操作都會(huì)導(dǎo)致代碼的膨脹和難以維護(hù),利用java8的Stream可以做如下簡(jiǎn)單實(shí)現(xiàn)

          long?time3=System.currentTimeMillis();
          long?res?=?LongStream.rangeClosed(1,?10000000000L).parallel().sum();
          System.out.println("stream計(jì)算結(jié)果為:"+res);
          System.out.println("stream耗時(shí)為:"+(System.currentTimeMillis()-time3));

          結(jié)果如下:

          串行計(jì)算結(jié)果為:-5340232216128654848
          串行耗時(shí):4631
          stream計(jì)算結(jié)果為:-5340232216128654848
          stream耗時(shí)為:3605

          雖然這里的耗時(shí)可能比不過(guò)我們直接手動(dòng)劃分,并發(fā)的方式去進(jìn)行計(jì)算,但是這里的代碼可讀性以及可拓展性是非常好的,如果你想過(guò)濾掉所有的奇數(shù),加一個(gè)filter就好。誠(chéng)然這個(gè)結(jié)果也受限于我僅僅只有四核的垃圾筆記本,無(wú)論如何,通過(guò)Stream的方式,Java的并行計(jì)算也變得簡(jiǎn)單!




          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ???

          ?長(zhǎng)按上方微信二維碼?2 秒


          感謝點(diǎn)贊支持下哈?

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲1区2区3区4区5区 | 成人在线三级片 | 舔逼网 | 五月丁香啪啪网 | 亚洲无码成人电影 |