<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>

          京東面試官:公司項(xiàng)目中 Java 的多線程一般用在哪些場(chǎng)景?

          共 6869字,需瀏覽 14分鐘

           ·

          2021-03-11 21:24

          2833ca36956f6df4ff9c6d820cc3f78a.webpdf9bfd95c9f062eba8523948a3fd6a42.webp

          戳這里,加關(guān)注哦~

          多線程使用的主要目的在于:

          1、吞吐量:你做WEB,容器幫你做了多線程,但是他只能幫你做請(qǐng)求層面的。簡(jiǎn)單的說,可能就是一個(gè)請(qǐng)求一個(gè)線程?;蚨鄠€(gè)請(qǐng)求一個(gè)線程。如果是單線程,那同時(shí)只能處理一個(gè)用戶的請(qǐng)求。

          2、伸縮性:也就是說,你可以通過增加CPU核數(shù)來提升性能。如果是單線程,那程序執(zhí)行到死也就利用了單核,肯定沒辦法通過增加CPU核數(shù)來提升性能。

          鑒于是做WEB的,第1點(diǎn)可能你幾乎不涉及。那這里我就講第二點(diǎn)吧。

          舉個(gè)簡(jiǎn)單的例子:

          假設(shè)有個(gè)請(qǐng)求,這個(gè)請(qǐng)求服務(wù)端的處理需要執(zhí)行3個(gè)很緩慢的IO操作(比如數(shù)據(jù)庫(kù)查詢或文件查詢),那么正常的順序可能是(括號(hào)里面代表執(zhí)行時(shí)間):

          1. 讀取文件1  (10ms)
          2. 處理1的數(shù)據(jù)(1ms)
          3. 讀取文件2  (10ms)
          4. 處理2的數(shù)據(jù)(1ms)
          5. 讀取文件3  (10ms)
          6. 處理3的數(shù)據(jù)(1ms)
          7. 整合1、2、3的數(shù)據(jù)結(jié)果 (1ms)

          單線程總共就需要34ms。

          那如果你在這個(gè)請(qǐng)求內(nèi),把a(bǔ)b、cd、ef分別分給3個(gè)線程去做,就只需要12ms了。

          所以多線程不是沒怎么用,而是,你平常要善于發(fā)現(xiàn)一些可優(yōu)化的點(diǎn)。然后評(píng)估方案是否應(yīng)該使用。假設(shè)還是上面那個(gè)相同的問題:但是每個(gè)步驟的執(zhí)行時(shí)間不一樣了。

          1. 讀取文件1  (1ms)
          2. 處理1的數(shù)據(jù)(1ms)
          3. 讀取文件2  (1ms)
          4. 處理2的數(shù)據(jù)(1ms)
          5. 讀取文件3  (28ms)
          6. 處理3的數(shù)據(jù)(1ms)
          7. 整合1、2、3的數(shù)據(jù)結(jié)果 (1ms)

          單線程總共就需要34ms。

          如果還是按上面的劃分方案(上面方案和木桶原理一樣,耗時(shí)取決于最慢的那個(gè)線程的執(zhí)行速度),在這個(gè)例子中是第三個(gè)線程,執(zhí)行29ms。那么最后這個(gè)請(qǐng)求耗時(shí)是30ms。比起不用單線程,就節(jié)省了4ms。但是有可能線程調(diào)度切換也要花費(fèi)個(gè)1、2ms。因此,這個(gè)方案顯得優(yōu)勢(shì)就不明顯了,還帶來程序復(fù)雜度提升。不太值得。

          那么現(xiàn)在優(yōu)化的點(diǎn),就不是第一個(gè)例子那樣的任務(wù)分割多線程完成。而是優(yōu)化文件3的讀取速度??赡苁遣捎镁彺婧蜏p少一些重復(fù)讀取。

          首先,假設(shè)有一種情況,所有用戶都請(qǐng)求這個(gè)請(qǐng)求,那其實(shí)相當(dāng)于所有用戶都需要讀取文件3。那你想想,100個(gè)人進(jìn)行了這個(gè)請(qǐng)求,相當(dāng)于你花在讀取這個(gè)文件上的時(shí)間就是28×100=2800ms了。那么,如果你把文件緩存起來,那只要第一個(gè)用戶的請(qǐng)求讀取了,第二個(gè)用戶不需要讀取了,從內(nèi)存取是很快速的,可能1ms都不到。

          偽代碼:

          public class MyServlet extends Servlet{
              private static Map<String, String> fileName2Data = new HashMap<String, String>();
              private void processFile3(String fName){
                  String data = fileName2Data.get(fName);
                  if(data==null){
                      data = readFromFile(fName); //耗時(shí)28ms
                      fileName2Data.put(fName, data);
                  }
                  //process with data
              }
          }


          看起來好像還不錯(cuò),建立一個(gè)文件名和文件數(shù)據(jù)的映射。如果讀取一個(gè)map中已經(jīng)存在的數(shù)據(jù),那么就不不用讀取文件了。

          可是問題在于,Servlet是并發(fā),上面會(huì)導(dǎo)致一個(gè)很嚴(yán)重的問題,死循環(huán)。因?yàn)椋琀ashMap在并發(fā)修改的時(shí)候,可能是導(dǎo)致循環(huán)鏈表的構(gòu)成?。。。ň唧w你可以自行閱讀HashMap源碼)如果你沒接觸過多線程,可能到時(shí)候發(fā)現(xiàn)服務(wù)器沒請(qǐng)求也巨卡,也不知道什么情況!

          好的,那就用ConcurrentHashMap,正如他的名字一樣,他是一個(gè)線程安全的HashMap,這樣能輕松解決問題。

          public class MyServlet extends Servlet{
              private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
              private void processFile3(String fName){
                  String data = fileName2Data.get(fName);
                  if(data==null){
                      data = readFromFile(fName); //耗時(shí)28ms
                      fileName2Data.put(fName, data);
                  }
                  //process with data
              }
          }


          這樣真的解決問題了嗎,這樣雖然只要有用戶訪問過文件a,那另一個(gè)用戶想訪問文件a,也會(huì)從fileName2Data中拿數(shù)據(jù),然后也不會(huì)引起死循環(huán)。

          可是,如果你覺得這樣就已經(jīng)完了,那你把多線程也想的太簡(jiǎn)單了,騷年!你會(huì)發(fā)現(xiàn),1000個(gè)用戶首次訪問同一個(gè)文件的時(shí)候,居然讀取了1000次文件(這是最極端的,可能只有幾百)。What the fuckin hell!!!

          難道代碼錯(cuò)了嗎,難道我就這樣過我的一生!

          好好分析下。Servlet是多線程的,那么

          public class MyServlet extends Servlet{
              private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
              private void processFile3(String fName){
                  String data = fileName2Data.get(fName);
                  //“偶然”-- 1000個(gè)線程同時(shí)到這里,同時(shí)發(fā)現(xiàn)data為null
                  if(data==null){
                      data = readFromFile(fName); //耗時(shí)28ms
                      fileName2Data.put(fName, data);
                  }
                  //process with data
              }
          }


          上面注釋的“偶然”,這是完全有可能的,因此,這樣做還是有問題。

          因此,可以自己簡(jiǎn)單的封裝一個(gè)任務(wù)來處理。

          public class MyServlet extends Servlet{
              private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>();
              private static ExecutorService exec = Executors.newCacheThreadPool();
              private void processFile3(String fName){
                  FutureTask data = fileName2Data.get(fName);
                  //“偶然”-- 1000個(gè)線程同時(shí)到這里,同時(shí)發(fā)現(xiàn)data為null
                  if(data==null){
                      data = newFutureTask(fName);
                      FutureTask old = fileName2Data.putIfAbsent(fName, data);
                      if(old==null){
                          data = old;
                      }else{
                          exec.execute(data);
                      }
                  }
                  String d = data.get();
                  //process with data
              }
               
              private FutureTask newFutureTask(final String file){
                  return  new FutureTask(new Callable<String>(){
                      public String call(){
                          return readFromFile(file);
                      }
           
                      private String readFromFile(String file){return "";}
                  }
              }
          }


          以上所有代碼都是直接在bbs打出來的,不保證可以直接運(yùn)行。

          多線程最多的場(chǎng)景:web服務(wù)器本身;各種專用服務(wù)器(如游戲服務(wù)器);

          多線程的常見應(yīng)用場(chǎng)景:

          • 后臺(tái)任務(wù),例如:定時(shí)向大量(100w以上)的用戶發(fā)送郵件;
          • 異步處理,例如:發(fā)微博、記錄日志等;
          • 分布式計(jì)算

          來源:cnblogs.com/kenshinobiy/p/4671314.html


          最后給大家送下福利,大家可以關(guān)注Java核心技術(shù)公眾號(hào),在后臺(tái)回復(fù) “福利”可以獲取一份我整理的最新Java面試題資料。

          最近好文分享1、一次畢生難忘的 Java 內(nèi)存泄漏排查經(jīng)歷!
          2、面試必問!JDK 中定時(shí)器是如何實(shí)現(xiàn)的?
          3、說實(shí)話!你知道 Java 中的回調(diào)機(jī)制嗎?
          4、IDEA 真牛逼,900行 "又臭又長(zhǎng)" 的類重構(gòu)!5、數(shù)數(shù) FastJson 那些年犯下的'血案'...6、Java 最多支持多少個(gè)線程?你會(huì)怎么答?
          ……

          更多請(qǐng)掃碼關(guān)注 Java核心技術(shù)一個(gè)分享Java核心技術(shù)干貨的公眾號(hào)
          點(diǎn)擊閱讀原文獲取面試題~
          瀏覽 28
          點(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>
                  大香蕉综合在线观看 | 东京热一区二区 | 蜜乳视频在线观看 | 好看的印度三色电费 | 台湾成人视频 |