精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

10問10答:你真的了解線程池嗎?

開發(fā) 開發(fā)工具
我在查找資料的過程中,發(fā)現(xiàn)有些問題存在爭議。后面發(fā)現(xiàn),一部分原因是因為不同JDK版本的現(xiàn)實是有差異的。因此,下面的分析是基于當下最常用的版本JDK1.8,并且對于存在爭議的問題,我們分析源碼,源碼才是最準確的。

 [[402940]]

《Java開發(fā)手冊》中強調(diào),線程資源必須通過線程池提供,而創(chuàng)建線程池必須使用ThreadPoolExecutor。手冊主要強調(diào)利用線程池避免兩個問題,一是線程過渡切換,二是避免請求過多時造成OOM。但是如果參數(shù)配置錯誤,還是會引發(fā)上面的兩個問題。所以本節(jié)我們主要是討論ThreadPoolExecutor的一些技術(shù)細節(jié),并且給出幾個常用的最佳實踐建議。

我在查找資料的過程中,發(fā)現(xiàn)有些問題存在爭議。后面發(fā)現(xiàn),一部分原因是因為不同JDK版本的現(xiàn)實是有差異的。因此,下面的分析是基于當下最常用的版本JDK1.8,并且對于存在爭議的問題,我們分析源碼,源碼才是最準確的。

1 corePoolSize=0會怎么樣

這是一個爭議點。我發(fā)現(xiàn)大部分博文,不論是國內(nèi)的還是國外的,都是這樣回答這個問題的:

  • 提交任務(wù)后,先判斷當前池中線程數(shù)是否小于corePoolSize,如果小于,則創(chuàng)建新線程執(zhí)行這個任務(wù)。
  • 否則,判斷等待隊列是否已滿,如果沒有滿,則添加到等待隊列。
  • 否則,判斷當前池中線程數(shù)是否大于maximumPoolSize,如果大于則拒絕。
  • 否則,創(chuàng)建一個新的線程執(zhí)行這個任務(wù)。

按照上面的描述,如果corePoolSize=0,則會判斷等待隊列的容量,如果還有容量,則排隊,并且不會創(chuàng)建新的線程。

—— 但其實,這是老版本的實現(xiàn)方式,從1.6之后,實現(xiàn)方式就變了。我們直接看execute的源碼(submit也依賴它),我備注出了關(guān)鍵一行:

  1. int c = ctl.get(); 
  2.         if (workerCountOf(c) < corePoolSize) { 
  3.             if (addWorker(command, true)) 
  4.                 return
  5.             c = ctl.get(); 
  6.         } 
  7.         if (isRunning(c) && workQueue.offer(command)) { 
  8.             int recheck = ctl.get(); 
  9.             if (! isRunning(recheck) && remove(command)) 
  10.                 reject(command); 
  11.             // 注意這一行代碼,添加到等待隊列成功后,判斷當前池內(nèi)線程數(shù)是否為0,如果是則創(chuàng)建一個firstTask為null的worker,這個worker會從等待隊列中獲取任務(wù)并執(zhí)行。 
  12.             else if (workerCountOf(recheck) == 0) 
  13.                 addWorker(nullfalse); 
  14.         } 
  15.         else if (!addWorker(command, false)) 
  16.             reject(command); 
  • 線程池提交任務(wù)后,首先判斷當前池中線程數(shù)是否小于corePoolSize。
  • 如果小于則嘗試創(chuàng)建新的線程執(zhí)行該任務(wù);否則嘗試添加到等待隊列。
  • 如果添加隊列成功,判斷當前池內(nèi)線程數(shù)是否為0,如果是則創(chuàng)建一個firstTask為null的worker,這個worker會從等待隊列中獲取任務(wù)并執(zhí)行。
  • 如果添加到等待隊列失敗,一般是隊列已滿,才會再嘗試創(chuàng)建新的線程。
  • 但在創(chuàng)建之前需要與maximumPoolSize比較,如果小于則創(chuàng)建成功。
  • 否則執(zhí)行拒絕策略。

上述問題需區(qū)分JDK版本。在1.6版本之后,如果corePoolSize=0,提交任務(wù)時如果線程池為空,則會立即創(chuàng)建一個線程來執(zhí)行任務(wù)(先排隊再獲取);如果提交任務(wù)的時候,線程池不為空,則先在等待隊列中排隊,只有隊列滿了才會創(chuàng)建新線程。

所以,優(yōu)化在于,在隊列沒有滿的這段時間內(nèi),會有一個線程在消費提交的任務(wù);1.6之前的實現(xiàn)是,必須等隊列滿了之后,才開始消費。

2 線程池創(chuàng)建之后,會立即創(chuàng)建核心線程么

之前有人問過我這個問題,因為他發(fā)現(xiàn)應用中有些Bean創(chuàng)建了線程池,但是這個Bean一般情況下用不到,所以咨詢我是否需要把這個線程池注釋掉,以減少應用運行時的線程數(shù)(該應用運行時線程過多。)

不會。從上面的源碼可以看出,在剛剛創(chuàng)建ThreadPoolExecutor的時候,線程并不會立即啟動,而是要等到有任務(wù)提交時才會啟動,除非調(diào)用了prestartCoreThread/prestartAllCoreThreads事先啟動核心線程。

  • prestartCoreThread:Starts a core thread, causing it to idly wait for work. This overrides the default policy of starting core threads only when new tasks are executed.
  • prestartAllCoreThreads:Starts all core threads.

3 核心線程永遠不會銷毀么

這個問題有點tricky。首先我們要明確一下概念,雖然在JavaDoc中也使用了“core/non-core threads”這樣的描述,但其實這是一個動態(tài)的概念,JDK并沒有給一部分線程打上“core”的標記,做什么特殊化的處理。這個問題我認為想要探討的是閑置線程終結(jié)策略的問題。

在JDK1.6之前,線程池會盡量保持corePoolSize個核心線程,即使這些線程閑置了很長時間。這一點曾被開發(fā)者詬病,所以從JDK1.6開始,提供了方法allowsCoreThreadTimeOut,如果傳參為true,則允許閑置的核心線程被終止。

請注意這種策略和corePoolSize=0的區(qū)別。我總結(jié)的區(qū)別是:

  • corePoolSize=0:在一般情況下只使用一個線程消費任務(wù),只有當并發(fā)請求特別多、等待隊列都滿了之后,才開始用多線程。
  • allowsCoreThreadTimeOut=true && corePoolSize>1:在一般情況下就開始使用多線程(corePoolSize個),當并發(fā)請求特別多,等待隊列都滿了之后,繼續(xù)加大線程數(shù)。但是當請求沒有的時候,允許核心線程也終止。

所以corePoolSize=0的效果,基本等同于allowsCoreThreadTimeOut=true && corePoolSize=1,但實現(xiàn)細節(jié)其實不同。

在JDK1.6之后,如果allowsCoreThreadTimeOut=true,核心線程也可以被終止。

4 如何保證線程不被銷毀

首先我們要明確一下線程池模型。線程池有個內(nèi)部類Worker,它實現(xiàn)了Runnable接口,首先,它自己要run起來。然后它會在合適的時候獲取我們提交的Runnable任務(wù),然后調(diào)用任務(wù)的run()接口。一個Worker不終止的話可以不斷執(zhí)行任務(wù)。

我們前面說的“線程池中的線程”,其實就是Worker;等待隊列中的元素,是我們提交的Runnable任務(wù)。

每一個Worker在創(chuàng)建出來的時候,會調(diào)用它本身的run()方法,實現(xiàn)是runWorker(this),這個實現(xiàn)的核心是一個while循環(huán),這個循環(huán)不結(jié)束,Worker線程就不會終止,就是這個基本邏輯。

  • 在這個while條件中,有個getTask()方法是核心中的核心,它所做的事情就是從等待隊列中取出任務(wù)來執(zhí)行:
  • 如果沒有達到corePoolSize,則創(chuàng)建的Worker在執(zhí)行完它承接的任務(wù)后,會用workQueue.take()取任務(wù)、注意,這個接口是阻塞接口,如果取不到任務(wù),Worker線程一直阻塞。
  • 如果超過了corePoolSize,或者allowCoreThreadTimeOut,一個Worker在空閑了之后,會用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)取任務(wù)。注意,這個接口只阻塞等待keepAliveTime時間,超過這個時間返回null,則Worker的while循環(huán)執(zhí)行結(jié)束,則被終止了。
  1. final void runWorker(Worker w) { 
  2.         Thread wt = Thread.currentThread(); 
  3.         Runnable task = w.firstTask; 
  4.         w.firstTask = null
  5.         w.unlock(); // allow interrupts 
  6.         boolean completedAbruptly = true
  7.         try { 
  8.             // 看這里,核心邏輯在這里 
  9.             while (task != null || (task = getTask()) != null) { 
  10.                 w.lock(); 
  11.                 // If pool is stopping, ensure thread is interrupted; 
  12.                 // if not, ensure thread is not interrupted.  This 
  13.                 // requires a recheck in second case to deal with 
  14.                 // shutdownNow race while clearing interrupt 
  15.                 if ((runStateAtLeast(ctl.get(), STOP) || 
  16.                      (Thread.interrupted() && 
  17.                       runStateAtLeast(ctl.get(), STOP))) && 
  18.                     !wt.isInterrupted()) 
  19.                     wt.interrupt(); 
  20.                 try { 
  21.                     beforeExecute(wt, task); 
  22.                     Throwable thrown = null
  23.                     try { 
  24.                         task.run(); 
  25.                     } catch (RuntimeException x) { 
  26.                         thrown = x; throw x; 
  27.                     } catch (Error x) { 
  28.                         thrown = x; throw x; 
  29.                     } catch (Throwable x) { 
  30.                         thrown = x; throw new Error(x); 
  31.                     } finally { 
  32.                         afterExecute(task, thrown); 
  33.                     } 
  34.                 } finally { 
  35.                     task = null
  36.                     w.completedTasks++; 
  37.                     w.unlock(); 
  38.                 } 
  39.             } 
  40.             completedAbruptly = false
  41.         } finally { 
  42.             processWorkerExit(w, completedAbruptly); 
  43.         } 
  44.     } 
  1. private Runnable getTask() { 
  2.         boolean timedOut = false; // Did the last poll() time out
  3.  
  4.         for (;;) { 
  5.             int c = ctl.get(); 
  6.             int rs = runStateOf(c); 
  7.  
  8.             // Check if queue empty only if necessary. 
  9.             if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { 
  10.                 decrementWorkerCount(); 
  11.                 return null
  12.             } 
  13.  
  14.             int wc = workerCountOf(c); 
  15.  
  16.             // Are workers subject to culling? 
  17.             boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; 
  18.  
  19.             if ((wc > maximumPoolSize || (timed && timedOut)) 
  20.                 && (wc > 1 || workQueue.isEmpty())) { 
  21.                 if (compareAndDecrementWorkerCount(c)) 
  22.                     return null
  23.                 continue
  24.             } 
  25.  
  26.             try { 
  27.                 // 注意,核心中的核心在這里 
  28.                 Runnable r = timed ? 
  29.                     workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : 
  30.                     workQueue.take(); 
  31.                 if (r != null
  32.                     return r; 
  33.                 timedOut = true
  34.             } catch (InterruptedException retry) { 
  35.                 timedOut = false
  36.             } 
  37.         } 
  38.     } 

實現(xiàn)方式非常巧妙,核心線程(Worker)即使一直空閑也不終止,是通過workQueue.take()實現(xiàn)的,它會一直阻塞到從等待隊列中取到新的任務(wù)。非核心線程空閑指定時間后終止是通過workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)實現(xiàn)的,一個空閑的Worker只等待keepAliveTime,如果還沒有取到任務(wù)則循環(huán)終止,線程也就運行結(jié)束了。

引申思考

Worker本身就是個線程,它再調(diào)用我們傳入的Runnable.run(),會啟動一個子線程么?如果你還沒有答案,再回想一下Runnable和Thread的關(guān)系。

5 空閑線程過多會有什么問題

籠統(tǒng)地回答是會占用內(nèi)存,我們分析一下占用了哪些內(nèi)存。首先,比較普通的一部分,一個線程的內(nèi)存模型:

  • 虛擬機棧
  • 本地方法棧
  • 程序計數(shù)器

我想額外強調(diào)是下面這幾個內(nèi)存占用,需要小心:

  • ThreadLocal:業(yè)務(wù)代碼是否使用了ThreadLocal?就算沒有,Spring框架中也大量使用了ThreadLocal,你所在公司的框架可能也是一樣。
  • 局部變量:線程處于阻塞狀態(tài),肯定還有棧幀沒有出棧,棧幀中有局部變量表,凡是被局部變量表引用的內(nèi)存都不能回收。所以如果這個線程創(chuàng)建了比較大的局部變量,那么這一部分內(nèi)存無法GC。
  • TLAB機制:如果你的應用線程數(shù)處于高位,那么新的線程初始化可能因為Eden沒有足夠的空間分配TLAB而觸發(fā)YoungGC。

線程池保持空閑的核心線程是它的默認配置,一般來講是沒有問題的,因為它占用的內(nèi)存一般不大。怕的就是業(yè)務(wù)代碼中使用ThreadLocal緩存的數(shù)據(jù)過大又不清理。

如果你的應用線程數(shù)處于高位,那么需要觀察一下YoungGC的情況,估算一下Eden大小是否足夠。如果不夠的話,可能要謹慎地創(chuàng)建新線程,并且讓空閑的線程終止;必要的時候,可能需要對JVM進行調(diào)參。

6 keepAliveTime=0會怎么樣

這也是個爭議點。有的博文說等于0表示空閑線程永遠不會終止,有的說表示執(zhí)行完立刻終止。還有的說等于-1表示空閑線程永遠不會終止。其實稍微看一下源碼知道了,這里我直接拋出答案。

在JDK1.8中,keepAliveTime=0表示非核心線程執(zhí)行完立刻終止。

默認情況下,keepAliveTime小于0,初始化的時候才會報錯;但如果allowsCoreThreadTimeOut,keepAliveTime必須大于0,不然初始化報錯。

7 怎么進行異常處理

很多代碼的寫法,我們都習慣按照常見范式去編寫,而沒有去思考為什么。比如:

  • 如果我們使用execute()提交任務(wù),我們一般要在Runable任務(wù)的代碼加上try-catch進行異常處理。
  • 如果我們使用submit()提交任務(wù),我們一般要在主線程中,對Future.get()進行try-catch進行異常處理。

—— 但是在上面,我提到過,submit()底層實現(xiàn)依賴execute(),兩者應該統(tǒng)一呀,為什么有差異呢?下面再扒一扒submit()的源碼,它的實現(xiàn)蠻有意思。

首先,ThreadPoolExecutor中沒有submit的代碼,而是在它的父類AbstractExecutorService中,有三個submit的重載方法,代碼非常簡單,關(guān)鍵代碼就兩行:

  1. public Future<?> submit(Runnable task) { 
  2.         if (task == null) throw new NullPointerException(); 
  3.         RunnableFuture<Void> ftask = newTaskFor(task, null); 
  4.         execute(ftask); 
  5.         return ftask; 
  6.     } 
  7.     public <T> Future<T> submit(Runnable task, T result) { 
  8.         if (task == null) throw new NullPointerException(); 
  9.         RunnableFuture<T> ftask = newTaskFor(task, result); 
  10.         execute(ftask); 
  11.         return ftask; 
  12.     } 
  13.     public <T> Future<T> submit(Callable<T> task) { 
  14.         if (task == null) throw new NullPointerException(); 
  15.         RunnableFuture<T> ftask = newTaskFor(task); 
  16.         execute(ftask); 
  17.         return ftask; 
  18.     } 

正是因為這三個重載方法,都調(diào)用了execute,所以我才說submit底層依賴execute。通過查看這里execute的實現(xiàn),我們不難發(fā)現(xiàn),它就是ThreadPoolExecutor中的實現(xiàn),所以,造成submit和execute的差異化的代碼,不在這。那么造成差異的一定在newTaskFor方法中。這個方法也就new了一個FutureTask而已,F(xiàn)utureTask實現(xiàn)RunnableFuture接口,RunnableFuture接口繼承Runnable接口和Future接口。而Callable只是FutureTask的一個成員變量。

所以講到這里,就有另一個Java基礎(chǔ)知識點:Callable和Future的關(guān)系。我們一般用Callable編寫任務(wù)代碼,F(xiàn)uture是異步返回對象,通過它的get方法,阻塞式地獲取結(jié)果。FutureTask的核心代碼就是實現(xiàn)了Future接口,也就是get方法的實現(xiàn):

  1. public V get() throws InterruptedException, ExecutionException { 
  2.         int s = state; 
  3.         if (s <= COMPLETING) 
  4.             // 核心代碼 
  5.             s = awaitDone(false, 0L); 
  6.         return report(s); 
  7.     } 
  8.  
  9.     private int awaitDone(boolean timed, long nanos) 
  10.         throws InterruptedException { 
  11.         final long deadline = timed ? System.nanoTime() + nanos : 0L; 
  12.         WaitNode q = null
  13.         boolean queued = false
  14.         // 死循環(huán) 
  15.         for (;;) { 
  16.             if (Thread.interrupted()) { 
  17.                 removeWaiter(q); 
  18.                 throw new InterruptedException(); 
  19.             } 
  20.  
  21.             int s = state; 
  22.             // 只有任務(wù)的狀態(tài)是’已完成‘,才會跳出死循環(huán) 
  23.             if (s > COMPLETING) { 
  24.                 if (q != null
  25.                     q.thread = null
  26.                 return s; 
  27.             } 
  28.             else if (s == COMPLETING) // cannot time out yet 
  29.                 Thread.yield(); 
  30.             else if (q == null
  31.                 q = new WaitNode(); 
  32.             else if (!queued) 
  33.                 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, 
  34.                                                      q.next = waiters, q); 
  35.             else if (timed) { 
  36.                 nanos = deadline - System.nanoTime(); 
  37.                 if (nanos <= 0L) { 
  38.                     removeWaiter(q); 
  39.                     return state; 
  40.                 } 
  41.                 LockSupport.parkNanos(this, nanos); 
  42.             } 
  43.             else 
  44.                 LockSupport.park(this); 
  45.         } 
  46.     } 

get的核心實現(xiàn)是有個awaitDone方法,這是一個死循環(huán),只有任務(wù)的狀態(tài)是“已完成”,才會跳出死循環(huán);否則會依賴UNSAFE包下的LockSupport.park原語進行阻塞,等待LockSupport.unpark信號量。而這個信號量只有當運行結(jié)束獲得結(jié)果、或者出現(xiàn)異常的情況下,才會發(fā)出來。分別對應方法set和setException。這就是異步執(zhí)行、阻塞獲取的原理,扯得有點遠了。

回到最初我們的疑問,為什么submit之后,通過get方法可以獲取到異常?原因是FutureTask有一個Object類型的outcome成員變量,用來記錄執(zhí)行結(jié)果。這個結(jié)果可以是傳入的泛型,也可以是Throwable異常:

  1. public void run() { 
  2.         if (state != NEW || 
  3.             !UNSAFE.compareAndSwapObject(this, runnerOffset, 
  4.                                          null, Thread.currentThread())) 
  5.             return
  6.         try { 
  7.             Callable<V> c = callable; 
  8.             if (c != null && state == NEW) { 
  9.                 V result; 
  10.                 boolean ran; 
  11.                 try { 
  12.                     result = c.call(); 
  13.                     ran = true
  14.                 } catch (Throwable ex) { 
  15.                     result = null
  16.                     ran = false
  17.                     setException(ex); 
  18.                 } 
  19.                 if (ran) 
  20.                     set(result); 
  21.             } 
  22.         } finally { 
  23.             // runner must be non-null until state is settled to 
  24.             // prevent concurrent calls to run() 
  25.             runner = null
  26.             // state must be re-read after nulling runner to prevent 
  27.             // leaked interrupts 
  28.             int s = state; 
  29.             if (s >= INTERRUPTING) 
  30.                 handlePossibleCancellationInterrupt(s); 
  31.         } 
  32.     } 
  33.  
  34.   // get方法中依賴的,報告執(zhí)行結(jié)果 
  35.     private V report(int s) throws ExecutionException { 
  36.         Object x = outcome; 
  37.         if (s == NORMAL) 
  38.             return (V)x; 
  39.         if (s >= CANCELLED) 
  40.             throw new CancellationException(); 
  41.         throw new ExecutionException((Throwable)x); 
  42.     } 

FutureTask的另一個巧妙的地方就是借用RunnableAdapter內(nèi)部類,將submit的Runnable封裝成Callable。所以就算你submit的是Runnable,一樣可以用get獲取到異常。

  • 不論是用execute還是submit,都可以自己在業(yè)務(wù)代碼上加try-catch進行異常處理。我一般喜歡使用這種方式,因為我喜歡對不同業(yè)務(wù)場景的異常進行差異化處理,至少打不一樣的日志吧。
  • 如果是execute,還可以自定義線程池,繼承ThreadPoolExecutor并復寫其afterExecute(Runnable r, Throwable t)方法。
  • 或者實現(xiàn)Thread.UncaughtExceptionHandler接口,實現(xiàn)void uncaughtException(Thread t, Throwable e);方法,并將該handler傳遞給線程池的ThreadFactory。
  • 但是注意,afterExecute和UncaughtExceptionHandler都不適用submit。因為通過上面的FutureTask.run()不難發(fā)現(xiàn),它自己對Throwable進行了try-catch,封裝到了outcome屬性,所以底層方法execute的Worker是拿不到異常信息的。

8 線程池需不需要關(guān)閉

一般來講,線程池的生命周期跟隨服務(wù)的生命周期。如果一個服務(wù)(Service)停止服務(wù)了,那么需要調(diào)用shutdown方法進行關(guān)閉。所以ExecutorService.shutdown在Java以及一些中間件的源碼中,是封裝在Service的shutdown方法內(nèi)的。

如果是Server端不重啟就不停止提供服務(wù),我認為是不需要特殊處理的。

9 shutdown和shutdownNow的區(qū)別

  • shutdown => 平緩關(guān)閉,等待所有已添加到線程池中的任務(wù)執(zhí)行完再關(guān)閉。
  • shutdownNow => 立刻關(guān)閉,停止正在執(zhí)行的任務(wù),并返回隊列中未執(zhí)行的任務(wù)。

本來想分析一下兩者的源碼的,但是發(fā)現(xiàn)本文的篇幅已經(jīng)過長了,源碼也貼了不少。感興趣的朋友自己看一下即可。

10 Spring中有哪些和ThreadPoolExecutor類似的工具

SimpleAsyncTaskExecutor 每次請求新開線程,沒有最大線程數(shù)設(shè)置.不是真的線程池,這個類不重用線程,每次調(diào)用都會創(chuàng)建一個新的線程。
SyncTaskExecutor 不是異步的線程同步可以用SyncTaskExecutor,但這個可以說不算一個線程池,因為還在原線程執(zhí)行。這個類沒有實現(xiàn)異步調(diào)用,只是一個同步操作。
ConcurrentTaskExecutor Executor的適配類,不推薦使用。如果ThreadPoolTaskExecutor不滿足要求時,才用考慮使用這個類。
SimpleThreadPoolTaskExecutor 監(jiān)聽Spring’s lifecycle callbacks,并且可以和Quartz的Component兼容.是Quartz的SimpleThreadPool的類。線程池同時被quartz和非quartz使用,才需要使用此類。

這里我想著重強調(diào)的就是SimpleAsyncTaskExecutor,Spring中使用的@Async注解,底層就是基于SimpleAsyncTaskExecutor去執(zhí)行任務(wù),只不過它不是線程池,而是每次都新開一個線程。

另外想要強調(diào)的是Executor接口。Java初學者容易想當然的以為Executor結(jié)尾的類就是一個線程池,而上面的都是反例。我們可以在JDK的execute方法上看到這個注釋:

  1. /** 
  2. * Executes the given command at some time in the future.  The command 
  3. * may execute in a new thread, in a pooled thread, or in the calling 
  4. * thread, at the discretion of the {@code Executor} implementation. 
  5. */ 

所以,它的職責并不是提供一個線程池的接口,而是提供一個“將來執(zhí)行命令”的接口。真正能代表線程池意義的,是ThreadPoolExecutor類,而不是Executor接口。

實踐總結(jié)

  • 【強制】使用ThreadPoolExecutor的構(gòu)造函數(shù)聲明線程池,避免使用Executors類的 newFixedThreadPool和newCachedThreadPool。
  • 【強制】 創(chuàng)建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。即threadFactory參數(shù)要構(gòu)造好。
  • 【建議】建議不同類別的業(yè)務(wù)用不同的線程池。
  • 【建議】CPU密集型任務(wù)(N+1):這種任務(wù)消耗的主要是CPU資源,可以將線程數(shù)設(shè)置為N(CPU核心數(shù))+1,比CPU核心數(shù)多出來的一個線程是為了防止線程偶發(fā)的缺頁中斷,或者其它原因?qū)е碌娜蝿?wù)暫停而帶來的影響。一旦任務(wù)暫停,CPU就會處于空閑狀態(tài),而在這種情況下多出來的一個線程就可以充分利用CPU的空閑時間。
  • 【建議】I/O密集型任務(wù)(2N):這種任務(wù)應用起來,系統(tǒng)會用大部分的時間來處理I/O交互,而線程在處理I/O的時間段內(nèi)不會占用CPU來處理,這時就可以將CPU交出給其它線程使用。因此在I/O密集型任務(wù)的應用中,我們可以多配置一些線程,具體的計算方法是2N。
  • 【建議】workQueue不要使用無界隊列,盡量使用有界隊列。避免大量任務(wù)等待,造成OOM。
  • 【建議】如果是資源緊張的應用,使用allowsCoreThreadTimeOut可以提高資源利用率。
  • 【建議】雖然使用線程池有多種異常處理的方式,但在任務(wù)代碼中,使用try-catch最通用,也能給不同任務(wù)的異常處理做精細化。
  • 【建議】對于資源緊張的應用,如果擔心線程池資源使用不當,可以利用ThreadPoolExecutor的API實現(xiàn)簡單的監(jiān)控,然后進行分析和優(yōu)化。

線程池初始化示例:

  1. private static final ThreadPoolExecutor pool; 
  2.  
  3.     static { 
  4.         ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("po-detail-pool-%d").build(); 
  5.         pool = new ThreadPoolExecutor(4, 8, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(512), 
  6.             threadFactory, new ThreadPoolExecutor.AbortPolicy()); 
  7.         pool.allowCoreThreadTimeOut(true); 
  8.     } 
  • threadFactory:給出帶業(yè)務(wù)語義的線程命名。
  • corePoolSize:快速啟動4個線程處理該業(yè)務(wù),是足夠的。
  • maximumPoolSize:IO密集型業(yè)務(wù),我的服務(wù)器是4C8G的,所以4*2=8。
  • keepAliveTime:服務(wù)器資源緊張,讓空閑的線程快速釋放。
  • pool.allowCoreThreadTimeOut(true):也是為了在可以的時候,讓線程釋放,釋放資源。
  • workQueue:一個任務(wù)的執(zhí)行時長在100~300ms,業(yè)務(wù)高峰期8個線程,按照10s超時(已經(jīng)很高了)。10s鐘,8個線程,可以處理10 * 1000ms / 200ms * 8 = 400個任務(wù)左右,往上再取一點,512已經(jīng)很多了。
  • handler:極端情況下,一些任務(wù)只能丟棄,保護服務(wù)端。 

 

責任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2021-06-03 14:23:57

線程線程池JAVA

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系統(tǒng)數(shù)據(jù)庫

2015-10-20 10:10:51

隱藏功能Windows 10微軟

2021-01-15 07:44:21

SQL注入攻擊黑客

2021-11-09 09:48:13

Logging python模塊

2019-09-16 08:40:42

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS網(wǎng)絡(luò)協(xié)議TCP

2023-03-16 10:49:55

2023-10-18 08:04:15

taskworker任務(wù)

2012-05-31 09:56:54

云安全

2022-12-12 08:46:11

2022-03-14 07:53:27

ELTETL大數(shù)據(jù)

2023-10-24 08:53:24

FutureTas并發(fā)編程

2015-07-31 10:35:18

實時計算

2017-10-18 22:01:12

2019-11-06 09:52:01

JavaScript單線程非阻塞

2019-07-25 07:42:35

程序員編程語言Python

2025-01-03 08:09:15

點贊
收藏

51CTO技術(shù)棧公眾號

亚洲欧洲精品一区二区精品久久久| www在线观看播放免费视频日本| 日韩精品看片| 色综合久久久久综合99| 色综合久久88色综合天天提莫| 欧美日韩人妻精品一区二区三区| 欧美无毛视频| 国产午夜精品一区二区三区视频 | 国产av无码专区亚洲av麻豆| 伊人久久亚洲热| 亚洲欧洲在线免费| 日韩在线不卡一区| 国产盗摄精品一区二区酒店| 久久久久久久久久久黄色 | 丁香婷婷激情网| 麻豆影院在线| 国产精品资源在线| 欧美资源在线观看| 亚洲狠狠婷婷综合久久久久图片| 男女在线观看视频| 国产精品1024| 欧美成人午夜免费视在线看片| 国产九九在线观看| 日本一级理论片在线大全| 久久伊人中文字幕| 亚洲一区二区少妇| 亚洲 日本 欧美 中文幕| 亚洲综合色网| 亚洲欧美另类在线观看| 国产成人亚洲精品无码h在线| 色一情一乱一乱一区91av| 免费观看成人av| 国内精品模特av私拍在线观看| 中文字幕永久免费| 91国内外精品自在线播放| 国产日韩精品一区二区浪潮av| 国产成人自拍视频在线观看| 国产第一页在线播放| 国产成人aa在线观看网站站| 欧美色视频在线| 黄色免费视频大全| 最新黄网在线观看| 国产精品国产三级国产普通话99| 91精品久久久久久久久久另类 | 国产成人av福利| 热99在线视频| 国产成人精品亚洲男人的天堂| 免费成人蒂法| 日韩欧美一区在线观看| 91精品999| 四虎av在线| 亚洲婷婷在线视频| 精品国产福利| 一级黄色av片| 丝袜亚洲另类欧美综合| 琪琪第一精品导航| 日韩特级黄色片| 亚洲第一在线| 久久久这里只有精品视频| 中文字幕av免费在线观看| 外国成人免费视频| 日韩中文在线视频| 中国免费黄色片| 97久久亚洲| 欧美三级日韩在线| 樱花www成人免费视频| 在线观看h片| 亚洲国产成人一区二区三区| 9a蜜桃久久久久久免费| 欧美极品aaaaabbbbb| 夜间精品视频| 久久97精品久久久久久久不卡| 免费在线观看你懂的| 日韩最新在线| 日韩乱码在线视频| 亚洲色成人网站www永久四虎| 日本在线一区二区三区| 91精品国产高清一区二区三区蜜臀 | 亚洲精品蜜桃久久久久久| 欧美日韩伦理片| 久久亚洲综合av| 日本一区二区三区视频免费看| 性猛交富婆╳xxx乱大交天津| 日韩精品视频网站| 国产欧美一区二区三区在线看| 日韩 欧美 精品| 国产视频一区在线观看一区免费| 精品国产一区二区三区四区在线观看 | 国产精品一区二区亚洲| 不卡在线一区| 久久精品免费播放| 蜜臀久久精品久久久用户群体| 亚洲调教一区| 色偷偷亚洲男人天堂| 91香蕉国产视频| 欧美一区视频| 992tv在线成人免费观看| 亚洲欧美一区二区三区四区五区| 青青草成人影院| 免费不卡在线观看av| 黄色激情视频在线观看| 午夜久久一区| 8x拔播拔播x8国产精品| 中文字幕精品视频在线观看| 精品一区二区三区视频在线观看 | 最新日韩三级| 欧美在线影院一区二区| 午夜xxxxx| 欧美成人一区在线观看| 中文字幕日韩精品在线| 国产一级在线视频| 日韩av不卡在线观看| 成人动漫在线观看视频| 欧美视频在线观看一区二区三区| 国产精品资源网| 欧美日韩国产精品一区二区| 香蕉视频成人在线| 国产精品久久久久久久久图文区| 亚洲电影一二三区| 成人黄色动漫| 欧美肥妇毛茸茸| 天堂久久久久久| 亚洲中无吗在线| 国产精品九九久久久久久久| 成人免费一级视频| 成人av在线资源网站| 亚洲黄色一区二区三区| 免费高清在线观看| 一本在线高清不卡dvd| 色姑娘综合天天| 国产欧美日韩一区二区三区四区| 亚洲天堂av在线免费| 久久久久成人精品无码| 久久福利视频一区二区| 欧美日本亚洲| 黄页视频在线播放| 在线亚洲高清视频| 自拍视频一区二区| 国产综合欧美| 国产一区二区在线免费视频| 男人的天堂av高清在线| 五月综合激情网| 日本美女久久久| 日本一区二区三区视频| 免费av在线一区| 一区二区三区日| 国产亚洲欧美在线| 97碰在线视频| 日韩精品三级| 久久久国产91| 欧美黄色免费看| 国产毛片精品视频| 韩国黄色一级大片| 中文在线аv在线| 欧美日韩成人高清| 国产真人真事毛片视频| 久久夜色精品| 日本日本精品二区免费| 中文在线免费二区三区| 亚洲激情自拍图| 国产一级淫片a| 成人黄色av电影| 日韩av电影免费观看| 乡村艳史在线观看| 日韩av网站在线| 日韩av女优在线观看| 成人精品一区二区三区中文字幕| 欧美视频观看一区| 国产拍在线视频| 欧美一区二区三区人| 欧美亚洲在线视频| 精品久久一二三| 亚洲十八**毛片| 日韩欧美国产wwwww| 日韩在线中文字幕视频| 国产美女精品在线| 日本a级片在线观看| 一区视频网站| 欧美成人h版在线观看| 亚洲av无码片一区二区三区| 亚洲影院免费观看| 国产二级一片内射视频播放| 国产精品久久777777毛茸茸| 久久精品女人的天堂av| 欧美自拍电影| xx视频.9999.com| 精品久久久久中文慕人妻| 亚洲一区影音先锋| 亚洲少妇第一页| **女人18毛片一区二区| 国产99视频在线观看| 成年人视频在线看| 91精品福利视频| 国产免费美女视频| 国产美女一区| 日韩一区二区电影在线观看| 超碰在线99| 国产亚洲欧洲高清一区| 国产一区二区在线不卡| 午夜精品爽啪视频| 久久国产波多野结衣| 99re这里只有精品6| 韩国视频一区二区三区| 99综合视频| 欧美一级爱爱视频| 欧美美女一区| 国产亚洲精品久久飘花| 成人精品在线| 国产精品日韩在线观看| 国产直播在线| 欧美成人中文字幕| aaa在线免费观看| 国产手机视频精品| 性生活三级视频| 欧美日韩成人在线| 香蕉污视频在线观看| 国产人成亚洲第一网站在线播放 | 日韩黄色在线观看| 被灌满精子的波多野结衣| 欧美国产一级| 欧美日韩一区在线播放| 美国成人xxx| 国产精品激情av在线播放| 鲁鲁在线中文| 欧美精品精品精品精品免费| 成人在线免费看片| 精品美女在线播放| 久久精品国产成人av| 亚洲国产裸拍裸体视频在线观看乱了| 日本黄色动态图| 日本欧美一区二区在线观看| 中文字幕欧美人与畜| 成人直播大秀| 图片区小说区区亚洲五月| 亚洲最大在线| 亚洲精品成人天堂一二三| 欧美两根一起进3p做受视频| 极品少妇一区二区三区| 国产欧美亚洲日本| 一区二区视频免费完整版观看| 久久久极品av| 黄色在线论坛| 久久亚洲国产精品| 国产色在线观看| 久久偷看各类女兵18女厕嘘嘘| 天天爱天天干天天操| 精品欧美乱码久久久久久| 亚洲第九十九页| 欧美色倩网站大全免费| 丰满人妻一区二区三区四区| 欧美性大战久久久| 中文天堂在线视频| 欧美麻豆精品久久久久久| 日本中文字幕第一页| 欧美日韩一二三四五区| 黄色免费av网站| 欧美亚洲综合一区| 91国产精品一区| 欧美一区二区三区免费视频| 日韩精品在线观看免费| 91久久久免费一区二区| 在线观看中文字幕2021| 91精品免费在线| 粉嫩小泬无遮挡久久久久久| 精品久久一二三区| 色猫av在线| 综合网中文字幕| 免费av在线电影| 日韩av影视综合网| 欧美成人免费| 色老头一区二区三区| 理论视频在线| 中文字幕在线成人| 精品日本一区二区| 国模大尺度视频一区二区| 7777精品久久久大香线蕉小说| 亚洲精品555| 5566日本婷婷色中文字幕97| 欧美精品高清| 亚洲999一在线观看www| 风间由美中文字幕在线看视频国产欧美| 国产欧美日韩最新| 亚洲一区二区三区四区电影| 精品人伦一区二区三区| 日韩理论电影| 少妇人妻无码专区视频| 日本伊人精品一区二区三区观看方式| 精品国产一二三四区| 老司机免费视频久久| 欧美国产在线一区| 本田岬高潮一区二区三区| 国产精品国产三级国产专业不| 久久久一区二区| 精品国产欧美日韩不卡在线观看 | 国产中文字幕久久| 亚洲激情中文1区| 亚洲av综合一区| 精品国产91亚洲一区二区三区婷婷 | 午夜看片在线免费| 午夜精品一区二区三区视频免费看| 欧美xxxx性xxxxx高清| 国产精品久久久久999| 日本欧美韩国| 国产久一道中文一区| 视频在线不卡免费观看| 亚洲国产精品电影| 丰满少妇xoxoxo视频| 91精品中文字幕一区二区三区| 中文字幕精品一区二区精| 精品剧情v国产在线观看在线| 日韩中文字幕综合| 播播国产欧美激情| 国产精欧美一区二区三区蓝颜男同| 热门国产精品亚洲第一区在线| 毛片无码国产| 成人资源av| 婷婷久久一区| 性生交免费视频| 99久久久精品免费观看国产蜜| 中文字幕在线免费看线人| 亚洲精品国久久99热| 久久亚洲成人av| 欧美男男青年gay1069videost| 国产伦一区二区| 中文字幕亚洲无线码a| 欧美自拍电影| 欧美精品人人做人人爱视频| 激情久久综合| 熟女人妻一区二区三区免费看| 91亚洲精品久久久蜜桃| 久久久久久久久福利| 亚洲美女在线国产| 中文字幕在线网站| 伊人精品在线观看| 日本aa在线| 91成人伦理在线电影| 91精品国产乱码久久久久久 | 国产在线观看a| 国产日韩欧美视频| 日韩一区欧美| 91亚洲精品久久久蜜桃借种| 国产精品天天看| 日本久久久久久久久| 欧美巨大xxxx做受沙滩| 91精品国产一区二区三区动漫| 免费av一区二区三区四区| 国产精品va无码一区二区| av一区二区三区| 在线天堂中文字幕| 日韩一级在线观看| 国产色a在线| 国产精品偷伦一区二区| 日韩88av| 国产精品久久久久久9999| 亚洲天堂av一区| 99久久久国产精品无码免费| 久久综合久久八八| 亚洲精选av| 久久久久久久午夜| 久久香蕉国产线看观看99| 亚洲不卡在线视频| 在线观看日韩专区| 国产麻豆精品| 成年女人18级毛片毛片免费| 波多野洁衣一区| 国产精品免费无遮挡无码永久视频| 精品久久人人做人人爰| 欧美xxxhd| 国产欧美日韩亚洲| 久久婷婷亚洲| 国产精品18在线| 精品美女一区二区三区| 亚洲一区资源| 福利网在线观看| 成人av在线网| wwwwww在线观看| 欧美巨猛xxxx猛交黑人97人| 另类在线视频| 色播五月激情五月| 亚洲一二三四在线| www.天堂av.com| 97精品伊人久久久大香线蕉| 国产精品午夜一区二区三区| 亚洲欧美天堂在线| 亚洲欧美影音先锋| 人人妻人人玩人人澡人人爽| 国产精品 欧美在线| 91国语精品自产拍| av在线网站观看| 福利微拍一区二区| 欧美a免费在线| 九九九久久久| 国产伦精品一区二区三区免费 | 欧美激情高清视频| 国产日产精品_国产精品毛片| 国产一区亚洲二区三区| 亚洲男人的天堂在线观看| 精品久久久久中文慕人妻| 国产成人福利网站| 欧美女人交a|