成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java 線程池 ThreadPoolExecutor 八種拒絕策略淺析

開發 后端
談到java的線程池最熟悉的莫過于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的這個api,大大的簡化了多線程代碼的開發。

 

前言

談到java的線程池最熟悉的莫過于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的這個api,大大的簡化了多線程代碼的開發。而不論你用FixedThreadPool還是CachedThreadPool其背后實現都是ThreadPoolExecutor。ThreadPoolExecutor是一個典型的緩存池化設計的產物,因為池子有大小,當池子體積不夠承載時,就涉及到拒絕策略。JDK中已經預設了4種線程池拒絕策略,下面結合場景詳細聊聊這些策略的使用場景,以及我們還能擴展哪些拒絕策略。

 

池化設計思想

池話設計應該不是一個新名詞。我們常見的如java線程池、jdbc連接池、redis連接池等就是這類設計的代表實現。這種設計會初始預設資源,解決的問題就是抵消每次獲取資源的消耗,如創建線程的開銷,獲取遠程連接的開銷等。就好比你去食堂打飯,打飯的大媽會先把飯盛好幾份放那里,你來了就直接拿著飯盒加菜即可,不用再臨時又盛飯又打菜,效率就高了。除了初始化資源,池化設計還包括如下這些特征:池子的初始值、池子的活躍值、池子的最大值等,這些特征可以直接映射到java線程池和數據庫連接池的成員屬性中。

線程池觸發拒絕策略的時機

和數據源連接池不一樣,線程池除了初始大小和池子最大值,還多了一個阻塞隊列來緩沖。數據源連接池一般請求的連接數超過連接池的最大值的時候就會觸發拒絕策略,策略一般是阻塞等待設置的時間或者直接拋異常。而線程池的觸發時機如下圖:

 

Java 線程池 ThreadPoolExecutor 八種拒絕策略淺析

 

如圖,想要了解線程池什么時候觸發拒絕粗略,需要明確上面三個參數的具體含義,是這三個參數總體協調的結果,而不是簡單的超過最大線程數就會觸發線程拒絕粗略,當提交的任務數大于corePoolSize時,會優先放到隊列緩沖區,只有填滿了緩沖區后,才會判斷當前運行的任務是否大于maxPoolSize,小于時會新建線程處理。大于時就觸發了拒絕策略,總結就是:當前提交任務數大于(maxPoolSize + queueCapacity)時就會觸發線程池的拒絕策略了。

JDK內置4種線程池拒絕策略

拒絕策略接口定義

在分析JDK自帶的線程池拒絕策略前,先看下JDK定義的 拒絕策略接口,如下:

  1. public interface RejectedExecutionHandler { 
  2.  void rejectedExecution(Runnable r, ThreadPoolExecutor executor); 

接口定義很明確,當觸發拒絕策略時,線程池會調用你設置的具體的策略,將當前提交的任務以及線程池實例本身傳遞給你處理,具體作何處理,不同場景會有不同的考慮,下面看JDK為我們內置了哪些實現:

CallerRunsPolicy(調用者運行策略)

  1. public static class CallerRunsPolicy implements RejectedExecutionHandler { 
  2.  public CallerRunsPolicy() { } 
  3.  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
  4.  if (!e.isShutdown()) { 
  5.  r.run(); 
  6.  } 
  7.  } 
  8.  } 
  • 功能:當觸發拒絕策略時,只要線程池沒有關閉,就由提交任務的當前線程處理。
  • 使用場景:一般在不允許失敗的、對性能要求不高、并發量較小的場景下使用,因為線程池一般情況下不會關閉,也就是提交的任務一定會被運行,但是由于是調用者線程自己執行的,當多次提交任務時,就會阻塞后續任務執行,性能和效率自然就慢了。

AbortPolicy(中止策略)

  1. public static class AbortPolicy implements RejectedExecutionHandler { 
  2.  public AbortPolicy() { } 
  3.  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
  4.  throw new RejectedExecutionException("Task " + r.toString() + 
  5.  " rejected from " + 
  6.  e.toString()); 
  7.  } 
  8.  } 
  • 功能:當觸發拒絕策略時,直接拋出拒絕執行的異常,中止策略的意思也就是打斷當前執行流程
  • 使用場景:這個就沒有特殊的場景了,但是一點要正確處理拋出的異常。ThreadPoolExecutor中默認的策略就是AbortPolicy,ExecutorService接口的系列ThreadPoolExecutor因為都沒有顯示的設置拒絕策略,所以默認的都是這個。但是請注意,ExecutorService中的線程池實例隊列都是無界的,也就是說把內存撐爆了都不會觸發拒絕策略。當自己自定義線程池實例時,使用這個策略一定要處理好觸發策略時拋的異常,因為他會打斷當前的執行流程。

DiscardPolicy(丟棄策略)

  1. public static class DiscardPolicy implements RejectedExecutionHandler { 
  2.  public DiscardPolicy() { } 
  3.  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
  4.  } 
  5.  } 
  • 功能:直接靜悄悄的丟棄這個任務,不觸發任何動作
  • 使用場景:如果你提交的任務無關緊要,你就可以使用它 。因為它就是個空實現,會悄無聲息的吞噬你的的任務。所以這個策略基本上不用了

DiscardOldestPolicy(棄老策略)

  1. public static class DiscardOldestPolicy implements RejectedExecutionHandler { 
  2.  public DiscardOldestPolicy() { } 
  3.  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
  4.  if (!e.isShutdown()) { 
  5.  e.getQueue().poll(); 
  6.  e.execute(r); 
  7.  } 
  8.  } 
  9.  } 
  • 功能:如果線程池未關閉,就彈出隊列頭部的元素,然后嘗試執行
  • 使用場景:這個策略還是會丟棄任務,丟棄時也是毫無聲息,但是特點是丟棄的是老的未執行的任務,而且是待執行優先級較高的任務。基于這個特性,我能想到的場景就是,發布消息,和修改消息,當消息發布出去后,還未執行,此時更新的消息又來了,這個時候未執行的消息的版本比現在提交的消息版本要低就可以被丟棄了。因為隊列中還有可能存在消息版本更低的消息會排隊執行,所以在真正處理消息的時候一定要做好消息的版本比較

第三方實現的拒絕策略

dubbo中的線程拒絕策略

  1. public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy { 
  2.  protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class); 
  3.  private final String threadName; 
  4.  private final URL url; 
  5.  private static volatile long lastPrintTime = 0; 
  6.  private static Semaphore guard = new Semaphore(1); 
  7.  public AbortPolicyWithReport(String threadName, URL url) { 
  8.  this.threadName = threadName; 
  9.  this.url = url; 
  10.  } 
  11.  @Override 
  12.  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
  13.  String msg = String.format("Thread pool is EXHAUSTED!" + 
  14.  " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," + 
  15.  " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!"
  16.  threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(), 
  17.  e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(), 
  18.  url.getProtocol(), url.getIp(), url.getPort()); 
  19.  logger.warn(msg); 
  20.  dumpJStack(); 
  21.  throw new RejectedExecutionException(msg); 
  22.  } 
  23.  private void dumpJStack() { 
  24.  //省略實現 
  25.  } 

可以看到,當dubbo的工作線程觸發了線程拒絕后,主要做了三個事情,原則就是盡量讓使用者清楚觸發線程拒絕策略的真實原因

  • 輸出了一條警告級別的日志,日志內容為線程池的詳細設置參數,以及線程池當前的狀態,還有當前拒絕任務的一些詳細信息。可以說,這條日志,使用dubbo的有過生產運維經驗的或多或少是見過的,這個日志簡直就是日志打印的典范,其他的日志打印的典范還有spring。得益于這么詳細的日志,可以很容易定位到問題所在
  • 輸出當前線程堆棧詳情,這個太有用了,當你通過上面的日志信息還不能定位問題時,案發現場的dump線程上下文信息就是你發現問題的救命稻草。
  • 繼續拋出拒絕執行異常,使本次任務失敗,這個繼承了JDK默認拒絕策略的特性

Netty中的線程池拒絕策略

  1. private static final class NewThreadRunsPolicy implements RejectedExecutionHandler { 
  2.  NewThreadRunsPolicy() { 
  3.  super(); 
  4.  } 
  5.  public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
  6.  try { 
  7.  final Thread t = new Thread(r, "Temporary task executor"); 
  8.  t.start(); 
  9.  } catch (Throwable e) { 
  10.  throw new RejectedExecutionException( 
  11.  "Failed to start a new thread", e); 
  12.  } 
  13.  } 
  14.  } 

Netty中的實現很像JDK中的CallerRunsPolicy,舍不得丟棄任務。不同的是,CallerRunsPolicy是直接在調用者線程執行的任務。而 Netty是新建了一個線程來處理的。所以,Netty的實現相較于調用者執行策略的使用面就可以擴展到支持高效率高性能的場景了。但是也要注意一點,Netty的實現里,在創建線程時未做任何的判斷約束,也就是說只要系統還有資源就會創建新的線程來處理,直到new不出新的線程了,才會拋創建線程失敗的異常

activeMq中的線程池拒絕策略

  1. new RejectedExecutionHandler() { 
  2.  @Override 
  3.  public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { 
  4.  try { 
  5.  executor.getQueue().offer(r, 60, TimeUnit.SECONDS); 
  6.  } catch (InterruptedException e) { 
  7.  throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker"); 
  8.  } 
  9.  throw new RejectedExecutionException("Timed Out while attempting to enqueue Task."); 
  10.  } 
  11.  }); 

activeMq中的策略屬于最大努力執行任務型,當觸發拒絕策略時,在嘗試一分鐘的時間重新將任務塞進任務隊列,當一分鐘超時還沒成功時,就拋出異常

pinpoint中的線程池拒絕策略

  1. public class RejectedExecutionHandlerChain implements RejectedExecutionHandler { 
  2.  private final RejectedExecutionHandler[] handlerChain; 
  3.  public static RejectedExecutionHandler build(List<RejectedExecutionHandler> chain) { 
  4.  Objects.requireNonNull(chain, "handlerChain must not be null"); 
  5.  RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]); 
  6.  return new RejectedExecutionHandlerChain(handlerChain); 
  7.  } 
  8.  private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) { 
  9.  this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null"); 
  10.  } 
  11.  @Override 
  12.  public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
  13.  for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) { 
  14.  rejectedExecutionHandler.rejectedExecution(r, executor); 
  15.  } 
  16.  } 

 

pinpoint的拒絕策略實現很有特點,和其他的實現都不同。他定義了一個拒絕策略鏈,包裝了一個拒絕策略列表,當觸發拒絕策略時,會將策略鏈中的rejectedExecution依次執行一遍

結語

前文從線程池設計思想,以及線程池觸發拒絕策略的時機引出java線程池拒絕策略接口的定義。并輔以JDK內置4種以及四個第三方開源軟件的拒絕策略定義描述了線程池拒絕策略實現的各種思路和使用場景。希望閱讀此文后能讓你對java線程池拒絕策略有更加深刻的認識,能夠根據不同的使用場景更加靈活的應用。

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2020-11-25 11:33:47

Java線程技術

2020-02-18 14:25:51

Java線程池拒絕策略

2024-10-09 15:58:02

2020-12-10 08:24:40

線程池線程方法

2021-09-11 07:32:15

Java線程線程池

2020-02-11 17:15:09

開發者拋棄 Executors

2015-10-10 09:39:42

Java線程池源碼解析

2024-11-11 17:27:45

2022-03-14 07:32:06

線程池拒絕策略自定義

2020-12-08 08:53:53

編程ThreadPoolE線程池

2024-04-26 00:00:02

Java線程池策略

2023-08-15 15:33:29

線程池線程數

2022-03-22 09:20:57

應用線程池技術

2009-08-19 10:34:16

反爬蟲

2009-07-09 10:28:19

線程池JDK5

2009-07-22 09:39:18

CLR線程池

2025-03-05 10:34:56

2024-01-08 09:09:40

線程池異常黑洞Futrue

2021-05-14 08:33:02

Flink策略源碼

2023-03-10 15:31:45

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 999国产精品视频 | 国产精品69久久久久水密桃 | 精品国产一二三区 | 亚洲精品一区二区三区蜜桃久 | 超碰在线免费 | 久久久久久影院 | 久久三级av | 亚洲一区导航 | 91精品国产一区二区三区 | 一级黄在线观看 | 免费视频一区二区 | 黄色一级网 | 日韩视频免费看 | 亚洲国产精品99久久久久久久久 | 成人免费av| 国产精品日韩欧美 | 欧美一区二区三区在线视频 | 亚洲免费大片 | 性视频一区 | www成人免费 | 81精品国产乱码久久久久久 | 超碰精品在线观看 | 有码在线 | 国产综合久久久久久鬼色 | 黄色免费观看 | 精品一区二区三区中文字幕 | 免费在线成人 | 亚洲精品一区二区在线 | 久久久久久国产 | 99精品国产一区二区三区 | 牛牛热在线视频 | 国产精品一区二区视频 | 精品久久久久久久久亚洲 | 国产成人精品一区二区三区在线 | 毛片在线免费播放 | 在线精品一区 | 国产精品成av人在线视午夜片 | 国产精品黄色 | 国产精品国产a级 | 99re视频在线观看 | 乱码av午夜噜噜噜噜动漫 |