日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java — 慎用Executors类中newFixedThreadPool()和newCachedThreadPool()

發布時間:2024/1/8 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java — 慎用Executors类中newFixedThreadPool()和newCachedThreadPool() 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • Executors.newCachedThreadPool()
        • 源碼
        • 分析
    • Executors.newFixedThreadPool()
        • 源碼
        • 分析
    • 避坑指南
        • 自定義線程池

在一些要求嚴格的公司,一般都明令禁止是使用Excutor提供的newFixedThreadPool()和newCachedThreadPool()直接創建線程池來操作線程,既然被禁止,那么就會有被禁止的道理,我們先來看一下之所以會被禁止的原因。

Executors.newCachedThreadPool()

源碼

/*** Creates a thread pool that creates new threads as needed, but* will reuse previously constructed threads when they are* available. These pools will typically improve the performance* of programs that execute many short-lived asynchronous tasks.* Calls to {@code execute} will reuse previously constructed* threads if available. If no existing thread is available, a new* thread will be created and added to the pool. Threads that have* not been used for sixty seconds are terminated and removed from* the cache. Thus, a pool that remains idle for long enough will* not consume any resources. Note that pools with similar* properties but different details (for example, timeout parameters)* may be created using {@link ThreadPoolExecutor} constructors.** @return the newly created thread pool*/public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

大致意思就是,通過該方法會創建一個線程池,當你執行一個任務,并且線程池中不存在可用的已構造好的線程時,它就會創建一個新線程,否則它會優先復用已有的線程,當線程未被使用時,默認 6 秒后被移除。
里面有一句話:
These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks.
這些線程池可以很明顯的提升那些短期存活的異步任務的執行效率
很明顯,官方標注他適合處理業務簡單、耗時短的任務,這是為什么呢?
我們接著看 ThreadPoolExecutor 構造方法的描述:

/*** Creates a new {@code ThreadPoolExecutor} with the given initial* parameters and default thread factory and rejected execution handler.* It may be more convenient to use one of the {@link Executors} factory* methods instead of this general purpose constructor.* 核心線程數* @param corePoolSize the number of threads to keep in the pool, even* if they are idle, unless {@code allowCoreThreadTimeOut} is set* 最大線程數* @param maximumPoolSize the maximum number of threads to allow in the* pool* 存活時長* @param keepAliveTime when the number of threads is greater than* the core, this is the maximum time that excess idle threads* will wait for new tasks before terminating.* 時間單位* @param unit the time unit for the {@code keepAliveTime} argument* 任務隊列* @param workQueue the queue to use for holding tasks before they are* executed. This queue will hold only the {@code Runnable}* tasks submitted by the {@code execute} method.* @throws IllegalArgumentException if one of the following holds:<br>* {@code corePoolSize < 0}<br>* {@code keepAliveTime < 0}<br>* {@code maximumPoolSize <= 0}<br>* {@code maximumPoolSize < corePoolSize}* @throws NullPointerException if {@code workQueue} is null*/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}

分析

結合ThreadPoolExecutor 構造方法的描述,我們可以知道,當我們調用newCachedThreadPool()方法的時候,它會創建一個核心線程數為 0 ,最大線程數為Integer上限,無用線程存活時間為 6 秒的線程池。
這意味著當我們需要在多線程中執行復雜業務時,它會瘋狂的創建線程,因為其他線程中的業務并未執行完。
例如下列代碼:

ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();for (int i = 0; i < 100000000; i++) {threadPool.execute(() -> {//DO SOMETHINGtry {TimeUnit.HOURS.sleep(1);} catch (InterruptedException e) {}});}threadPool.shutdown();threadPool.awaitTermination(1, TimeUnit.HOURS);

模擬瞬間創建100000000十萬個任務,且每個任務需要等待一秒鐘,會發現電腦內存使用率迅速增加并一直持續到 OOM。

Executors.newFixedThreadPool()

我們再來看一下源碼

源碼

/*** Creates a thread pool that reuses a fixed number of threads* operating off a shared unbounded queue. At any point, at most* {@code nThreads} threads will be active processing tasks.* If additional tasks are submitted when all threads are active,* they will wait in the queue until a thread is available.* If any thread terminates due to a failure during execution* prior to shutdown, a new one will take its place if needed to* execute subsequent tasks. The threads in the pool will exist* until it is explicitly {@link ExecutorService#shutdown shutdown}.** @param nThreads the number of threads in the pool* @return the newly created thread pool* @throws IllegalArgumentException if {@code nThreads <= 0}*/ public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()); }

大致描述是:創建一個由固定數量線程并在共享無界隊列上運行的線程池,在任何時候都最多只有nThreads個線程存在并執行任務。
如果在任務提交時,所有線程都在工作中,則會將該任務放入到隊列中等待,直到有可用的線程。如果某個線程在執行過程中出現異常,那么這個線程會終止,并且會有一個新的線程代替它進行后續的工作,線程池中的線程會一直存在直到線程池被明確的停止掉。

//停止接收新的任務,并繼續完成正在執行的任務和隊列中的任務 ExecutorService#shutdown//等所有已提交的任務(包括正在跑的和隊列中等待的)執行完 //或者等超時時間到 //或者線程被中斷,拋出InterruptedException ExecutorService#awaitTermination(1, TimeUnit.HOURS);//停止接受新的任務,忽略隊列中的任務并嘗試終止正在執行的任務 ExecutorService#shutdownNow

分析

通過源碼我們可以看出,該方法創建一個固定核心線程數和線程池大小的線程池,并且核心數等于最大線程數??雌饋砗孟駴]有類似newCachedThreadPool無限創建線程的情況,但是在他的描述中有一點很引人注意,
operating off a shared unbounded queue
操作一個共享無界的隊列
通過查看newFixedThreadPool()在創建線程池時傳入的隊列 new LinkedBlockingQueue()

public LinkedBlockingQueue() {this(Integer.MAX_VALUE);}

會發現,這個隊列的最大長度時Integer.MAX_VALUE,這就意味著,未能及時執行的任務都將添加到這個隊列里面
隨著任務的增加,這個隊列所占用的內存將越來越多。最終導致OOM也是遲早的事情。

避坑指南

對于線程池這種東西,其實讓我們自己去控制是最好的,我們可以通過實現自定義的線程池提供線程,不僅可以定制化的獲取線程執行過程中的狀態等信息,還能根據不同的任務使用不同的線程池。
例如,一條簡單的 查詢操作 和 文件讀取操作 就應該放在不同的線程池里面
因為如果兩種任務在同一個線程池里面,文件操作本身就是耗時的,它占用了線程之后會導致查詢操作等待或者直接被丟棄(取決于自定義線程池任務添加時的規則),這樣嚴重影響了查詢性能。

自定義線程池

//隊列長度為100BlockingQueue<Runnable> blockqueue = new LinkedBlockingQueue<Runnable>(100) {/*** 這里重寫offer方法* 在接收到新的任務時,會先加入到隊列中,當隊列滿了之后,才會創建新的線程 直到達到線程池的最大線程數* 我們現在需要接收到新任務時,優先將線程數擴容到最大數,后續任務再放入到隊列中* 加入隊列會調用 offer方法 ,我們直接返回false,制造隊列已滿的假象*/@Overridepublic boolean offer(Runnable e) {return false;}};ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10,10, TimeUnit.SECONDS,blockqueue , new ThreadFactoryBuilder().setNameFormat("mypool-%d").get(), (r, executor) -> {/*** 這里拒絕策略,被拒絕的任務會走該方法 及沒添加到隊列中,且沒有獲取到線程的任務* 因為我們設置的隊列中 offer方法固定返回false*/try {//如果允許該任務執行但是不阻塞,及如果進不了隊列就放棄,我們可以調用 offer 的另一個多參的方法if (!executor.getQueue().offer(r, 0, TimeUnit.SECONDS)) {throw new RejectedExecutionException("ThreadPool queue full, failed to offer " + r.toString());}//如果我們需要讓任務一定要執行,及足協而等待進入隊列,可以使用putexecutor.getQueue().put(r)} catch (InterruptedException e) {Thread.currentThread().interrupt();}});

總結

以上是生活随笔為你收集整理的Java — 慎用Executors类中newFixedThreadPool()和newCachedThreadPool()的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。