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

          螞蟻金服后端一面:Java怎么使用線程池/理解/處理什么問(wèn)題

          共 15416字,需瀏覽 31分鐘

           ·

          2021-06-08 19:51

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          真香!24W字的Java面試手冊(cè)(點(diǎn)擊查看)


          什么是線程池呢,字面意思,池是一個(gè)容器,比如我們之前還學(xué)過(guò)JDBC的連接池,還有內(nèi)存池,對(duì)象池等等。

          線程池就是事先準(zhǔn)備一些線程資源,需要的時(shí)候拿去用,用完的時(shí)候還回來(lái)。

          在之前有個(gè)推文講了SpringBoot中集成的線程池用法

          SpringBoot中的線程池,你真的會(huì)用么?

          線程池好處

          每次創(chuàng)建線程和銷(xiāo)毀線程都是消耗一定的資源,將線程統(tǒng)一進(jìn)行管理,可以大大減少了創(chuàng)建/銷(xiāo)毀的次數(shù),而且還能提高響應(yīng)的速度。另外多個(gè)線程去爭(zhēng)奪CPU資源時(shí)會(huì)造成堵塞,線程池可以統(tǒng)一對(duì)資源進(jìn)行分配,提高了線程的穩(wěn)定性和可控制性。

          總結(jié):

          • 每個(gè)線程都可以被重復(fù)利用,減少了創(chuàng)建和銷(xiāo)毀線程的次數(shù)。降低資源消耗
          • 靈活的調(diào)整線程的數(shù)量,方便管理
          • 提高響應(yīng)速度

          在面試中,經(jīng)常會(huì)問(wèn)到線程池的 四大方法、七個(gè)參數(shù)、四種拒絕策略

          四大方法

          即創(chuàng)建線程池的四種方法,如何創(chuàng)建線程池呢,很多人使用Executors來(lái)創(chuàng)建線程池,比如

          ExecutorService threadPool = Executors.newFixedThreadPool(5);

          創(chuàng)建線程有四種方法

          • newFixedThreadPool:創(chuàng)建固定大小的線程池

          • newSingleThreadExecutor:創(chuàng)建只有一個(gè)線程的線程池,確保任務(wù)串行執(zhí)行

          • newCachedThreadPool:創(chuàng)建一個(gè)不限制線程數(shù)的線程池。如果當(dāng)前線程池的規(guī)模超出了處理需求,將回收空的線程;當(dāng)需求增加時(shí),會(huì)增加線程數(shù)量;線程池規(guī)模無(wú)限制。

          • newScheduledThreadPool(不是很常用,適用于特定場(chǎng)景):創(chuàng)建一個(gè)固定長(zhǎng)度的線程池,而且以延遲或者定時(shí)的方式來(lái)執(zhí)行

          四個(gè)方法的源碼如下。

          // 1
          public static ExecutorService newFixedThreadPool(int nThreads) {
                  return new ThreadPoolExecutor(nThreads, nThreads,
                                                0L, TimeUnit.MILLISECONDS,
                                                new LinkedBlockingQueue<Runnable>());
          }

          // 2
          public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
                  return new FinalizableDelegatedExecutorService
                      (new ThreadPoolExecutor(11,
                                              0L, TimeUnit.MILLISECONDS,
                                              new LinkedBlockingQueue<Runnable>(),
                                              threadFactory));
          }

          // 3
          public static ExecutorService newCachedThreadPool() {
                  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                                60L, TimeUnit.SECONDS,
                                                new SynchronousQueue<Runnable>());
          }

          // 4
          public static ScheduledExecutorService newScheduledThreadPool(
                      int corePoolSize, ThreadFactory threadFactory)
           
          {
                  return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
          }

          從源碼中可以看到除了最后一個(gè)newScheduledThreadPool,其余的都是 return 了一個(gè)ThreadPoolExecutor對(duì)象。

          所以,開(kāi)啟線程池的本質(zhì)就是調(diào)用了ThreadPoolExecutor方法

          在阿里巴巴開(kāi)發(fā)手冊(cè)中明確規(guī)定:

          【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這 樣的處理方式讓寫(xiě)的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。說(shuō)明:Executors 返回的線程池對(duì)象的弊端如下:

          1. FixedThreadPool 和 SingleThreadPool:

          允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求,從而導(dǎo)致 OOM。

          1. CachedThreadPool:

          允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM。

          如果你的IDE安裝了阿里巴巴編碼規(guī)約的插件你會(huì)發(fā)現(xiàn)會(huì)有這樣的提示因此創(chuàng)建線程正確的方法是使用ThreadPoolExecutor來(lái)手動(dòng)創(chuàng)建(newScheduledThreadPool除外),這樣可以更好的規(guī)避資源耗盡的風(fēng)險(xiǎn)。

          public ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue,
                                    ThreadFactory threadFactory,
                                    RejectedExecutionHandler handler)
           
          {
                  this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                       threadFactory, handler);
          }


          public ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue)
           
          {
                  this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                       Executors.defaultThreadFactory(), defaultHandler);
          }

          從上面看,ThreadPoolExecutor有7個(gè)參數(shù),對(duì)應(yīng)的含義如下:

          • int corePoolSize:核心線程池大小
          • int maximumPoolSize:最大線程池大小
          • long keepAliveTime:線程最大的空閑時(shí)間(標(biāo)記線程空閑多久釋放)
          • TimeUnit unit:keepAliveTime的時(shí)間單位
          • BlockingQueue wordQueue:阻塞隊(duì)列
          • ThreadFactory threadFactory:線程的創(chuàng)建工廠,一般不用動(dòng)
          • RejectedExecutionHandle:拒絕策略

          newSingleThreadExecutor(創(chuàng)建只有一個(gè)線程的線程池) 默認(rèn)核心線程池大小是1,最大線程池也是1,而創(chuàng)建固定大小線程池的newFixedThreadPoolnewSingleThreadExecutor基本一樣,只不過(guò)把線程池大小和最大線程池大小動(dòng)態(tài)化了。

          public static ExecutorService newFixedThreadPool(int nThreads) {
                  return new ThreadPoolExecutor(nThreads, nThreads,....);
          }

          // 2
          public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
                  return new FinalizableDelegatedExecutorService
                      (new ThreadPoolExecutor(11,..,threadFactory));
          }

          newCachedThreadPool,核心線程池大小是0,但是默認(rèn)最大線程池大小是Integer.MAX_VALUE ,這是一個(gè)億級(jí)的數(shù)值,如果一個(gè)服務(wù)器上有上億個(gè)線程那消耗的資源必然是非常大的,因此正如前面講到的阿里開(kāi)發(fā)規(guī)范中建議通過(guò)ThreadPoolExecutor來(lái)手動(dòng)創(chuàng)建線程,指定相應(yīng)的參數(shù),避免發(fā)生OOM。

          public static ExecutorService newCachedThreadPool() {
                  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                                60L, TimeUnit.SECONDS,
                                                new SynchronousQueue<Runnable>());}

          舉個(gè)??

          咱們?nèi)ャy行辦理業(yè)務(wù)。先抽號(hào),然后在候客區(qū)等著叫號(hào),假設(shè)有6個(gè)窗口,因?yàn)榻裉烊瞬皇呛芏?,其?個(gè)窗口關(guān)閉了,只開(kāi)了2個(gè)辦理業(yè)務(wù)的窗口

          關(guān)于核心線程池/ 最大線程池/ 空閑時(shí)間 / 阻塞隊(duì)列 / 拒絕策略 ,下圖應(yīng)該很形象了。

          拒絕策略:

          查看源代碼發(fā)現(xiàn) RejectedExecutionHandler 有四種實(shí)現(xiàn),也就是四種拒絕策略。

          四種拒絕策略分別有什么用呢?還是從源碼上來(lái)看。

          AbortPolicy

          直接throw 一個(gè)異常,不做處理

          public static class AbortPolicy implements RejectedExecutionHandler {
           
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              throw new RejectedExecutionException("Task " + r.toString() +
                                                   " rejected from " +
                                                   e.toString());
            }
          }

          CallerRunsPolicy

          如果添加線程池失敗,就把任務(wù)交給主線程去執(zhí)行。好比排隊(duì)領(lǐng)妹子,你不讓我排隊(duì),那我自己去找妹子.

          public static class CallerRunsPolicy implements RejectedExecutionHandler {


            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              if (!e.isShutdown()) {
                r.run();
              }
            }
          }

          DiscardOldestPolicy

          如果阻塞隊(duì)列滿了,就把最開(kāi)始加入隊(duì)列的任務(wù)移除出去。再?lài)L試加入隊(duì)列。

          public static class DiscardOldestPolicy implements RejectedExecutionHandler {

            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
              if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
              }
            }
          }

          DiscardPolicy

          不做任何處理,rejectedExecution是一個(gè)空方法

          public static class DiscardPolicy implements RejectedExecutionHandler {

            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            }
          }

          ok!每個(gè)參數(shù)都搞明白了,用代碼實(shí)現(xiàn)一下上面的去銀行辦業(yè)務(wù)的例子

          package bilibili;

          import java.awt.desktop.AboutHandler;
          import java.util.concurrent.*;

          public class Pool {
              public static void main(String[] args) {
                  ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                          2// 核心線程2
                          6// 最大線程6
                          3// 最大空閑時(shí)間3秒
                          TimeUnit.SECONDS, // 時(shí)間單位秒
                          new LinkedBlockingDeque<>(4), // 阻塞隊(duì)列,容量4
                          Executors.defaultThreadFactory(), // 默認(rèn)的創(chuàng)建工廠
                          new ThreadPoolExecutor.AbortPolicy() //AbortPolicy拒絕策略,拋出異常
                  );

                  try {
                      for (int i = 0; i < 5; i++) {
                          threadPool.execute(new Runnable() {
                              @Override
                              public void run() {
                                  System.out.println("正在執(zhí)行: " + Thread.currentThread().getName());
                              }
                          });
                      }
                  } catch (Exception e) {
                      e.printStackTrace();
                  }finally {
                      threadPool.shutdown();
                  }
              }
          }

          運(yùn)行結(jié)果

          正在執(zhí)行: pool-1-thread-2
          正在執(zhí)行: pool-1-thread-1
          正在執(zhí)行: pool-1-thread-2
          正在執(zhí)行: pool-1-thread-2
          正在執(zhí)行: pool-1-thread-1

          可以看到在for循環(huán)中i < 5,沒(méi)有超過(guò)最大線程,此時(shí)使用的是 2 個(gè)核心線程在執(zhí)行

          當(dāng) i < 7,大于總線程數(shù),但是小于總線程+ 阻塞線程(6 + 4)

          此時(shí),使用了pool-1-thread-3線程

          如果正好10個(gè)線程,也就是最大線程數(shù)6  + 阻塞線程4。剛好夠用,

          上面寫(xiě)法過(guò)于臃腫,這里使用函數(shù)式編程的方法

          //其他代碼省略
          try {
            for (int i = 0; i < 10; i++) {
              threadPool.execute(()-> {
                System.out.println("正在執(zhí)行: " + Thread.currentThread().getName());
              });
            }
          catch (Exception e) {
            e.printStackTrace();
          }finally {
            threadPool.shutdown();
          }

          執(zhí)行結(jié)果:

          可以看到線程池中的 6 個(gè)線程都被用到了

          正在執(zhí)行: pool-1-thread-4
          正在執(zhí)行: pool-1-thread-4
          正在執(zhí)行: pool-1-thread-4
          正在執(zhí)行: pool-1-thread-4
          正在執(zhí)行: pool-1-thread-4
          正在執(zhí)行: pool-1-thread-1
          正在執(zhí)行: pool-1-thread-2
          正在執(zhí)行: pool-1-thread-5
          正在執(zhí)行: pool-1-thread-6
          正在執(zhí)行: pool-1-thread-3

          但是!當(dāng)循環(huán)11次,也就是11個(gè)人來(lái)辦理業(yè)務(wù)時(shí),因?yàn)槌^(guò)了最大的線程數(shù) + 阻塞線程,就會(huì)觸發(fā) 拒絕策略。

          因?yàn)樵诙x的時(shí)候使用的是AbortPolicy,因此會(huì)拋出異常。

          image-20210601163032900

          如果此時(shí)拒絕策略,改為CallerRunsPolicy, 也就是說(shuō)多出來(lái)的一個(gè)人交給主線程去執(zhí)行。

          拒絕策略為DiscardOldestPolicy或者DiscardPolicy時(shí),控制臺(tái)只會(huì)輸出10條結(jié)果。區(qū)別在于DiscardOldestPolicy會(huì)把最開(kāi)始加入隊(duì)列的任務(wù)移除出去,嘗試去競(jìng)爭(zhēng)線程池資源。而DiscardPolicy則將最后多出來(lái)的那一個(gè)不做任何處理。被干掉了。

          本文主要講了線程池的作用,以及三種創(chuàng)建方法,7個(gè)參數(shù),4種拒絕策略。這在面試中是經(jīng)常問(wèn)到的。如有不對(duì)之處歡迎指出。

          如有文章對(duì)你有幫助,

          歡迎關(guān)注??、點(diǎn)贊??、轉(zhuǎn)發(fā)??!



          推薦, Java面試手冊(cè) 
          內(nèi)容包括網(wǎng)絡(luò)協(xié)議、Java基礎(chǔ)、進(jìn)階、字符串、集合、并發(fā)、JVM、數(shù)據(jù)結(jié)構(gòu)、算法、MySQL、Redis、Mongo、Spring、SpringBoot、MyBatis、SpringCloud、Linux以及各種中間件(Dubbo、Nginx、Zookeeper、MQ、Kafka、ElasticSearch)等等...

          點(diǎn)擊文末“閱讀原文”可直達(dá)

          瀏覽 177
          點(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>
                  日逼天堂| 欧美日本韩国激情视频 | 你懂的 91 | 大色播国产精品 | 五月天自拍视频 |