1. 程式人生 > >解決java.util.concurrent.RejectedExecutionException

解決java.util.concurrent.RejectedExecutionException

前言

昨晚12:00執行自動化測試指令碼時遇到了java.util.concurrent.RejectedExecutionException這個異常,從異常名稱裡很容易分析出是提交的任務被執行緒池拒絕了。檢視原始碼發現是在Activity裡,AsyncTask是在自定義的執行緒池的執行的,但是onDestory函式裡卻是先顯示呼叫了執行緒池的shutdown方法,然後才是AsyncTask的cancel操作,因此可能導致任務被拒絕。

ThreadPoolExecutor

一個ExecutorService,它使用可能的幾個執行緒池之一執行每個提交的任務,通常使用Executors工廠方法配置,但是檢視原始碼,發現工廠方法也是統一呼叫了ThreadPoolExecutor類,以為例,原始碼如下:
public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
}
雖然我一直認同程式設計師應使用較為方便的Executors工廠方法Executors.newCachedThreadPool() (無界執行緒池,可以進行自動執行緒回收)、Executors.newFixedThreadPool(int)(固定大小執行緒池)和Executors.newSingleThreadExecutor()(單個後臺執行緒),但是通過原始碼我們可以發現最後他們均呼叫了ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) 方法,因此我們在分析java.util.concurrent.RejectedExecutionException之前,需要深入學習一下ThreadPoolExecutor的使用。

核心池和最大池的大小

TreadPoolExecutor將根據corePoolSize和maximumPoolSize設定的邊界自動調整池大小。當新任務在方法execute(java.lang.Runnable)中提交時,如果執行的執行緒少於corePoolSize,則建立新執行緒來處理請求,即使其他輔助執行緒是空閒的。如果執行的執行緒多於corePoolSize而少於maximumPoolSize,則僅當佇列滿時才建立新的執行緒。如果設定的corePoolSize和maximumPoolSize相同,則建立了固定大小的執行緒池。如果將maximumPoolSize設定為基本的無界值(如Integer.MAX_VALUE),則允許執行緒池適應任意數量的併發任務。

保持活動時間

如果池中當前有多於corePoolSize的執行緒,則這些多出的執行緒在空閒時間超過keepAliveTime時將會終止。

排隊

所有BlockingQueue都可用於傳輸和保持提交的任務。可以使用此佇列與池大小進行互動:
  • 如果執行的執行緒少於corePoolSize,則Executor始終首選新增新的執行緒,而不進行排隊。
  • 如果執行的執行緒等於或多於corePoolSize,則Executor始終首選將請求加入佇列,而不新增新的執行緒。
  • 如果無法將請求加入佇列,則建立新的執行緒,除非建立此執行緒超出maximumPoolSize,在這種情況下,任務將被拒絕(丟擲RejectedExecutionException)
排隊有三種通用策略:
  1. 直接提交。工作佇列的預設選項是synchronousQueue,它將任務直接提交給執行緒而不保持它們。在此,如果不存在可用於立即執行任務的執行緒,則試圖把任務加入佇列將失敗,因此會構造一個新的執行緒。此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。直接提交通常要求無界maximumPoolSizes以避免拒絕新提交的任務。當命令以超過佇列所能處理的平均數連續到達時,此策略允許無界執行緒具有增加的可能性。
  2. 無界佇列。使用無界佇列(例如,不具有預定義容量的LinkedBlockingQueue)將導致在所有corePoolSize執行緒都忙時新任務在佇列中等待。這樣,建立的執行緒就不會超過corePoolSize(因此,maximumPoolSize的值也就無效了)。
  3. 有界佇列。當使用有限的maximumPoolSizes時,有界佇列(如ArrayBlockingQueue)有助於防止資源耗盡,但是可能較難調整和控制。佇列大小和最大池大小可能需要相互折衷:使用大型佇列和小型池可以最大限度的降低CPU使用率、作業系統資源和上下文切換開銷,但是可能導致人工降低吞吐量。如果任務頻繁阻塞,則系統可能為超過您許可的更多執行緒安排時間,使用小型佇列通常要求較大的池大小,CPU使用率較高,但是可能遇到不可接受的排程開銷,這樣可會降低吞吐量。

終止

程式不再引用的池沒有剩餘執行緒會自動shutdown。如果希望確保回收取消引用的池(即使使用者忘記呼叫shutdown()),則必須安排未使用的執行緒最終終止。

分析

通過對ThreadPoolExecutor類分析,引發java.util.concurrent.RejectedExecutionException主要有兩種原因:1. 執行緒池顯示的呼叫了shutdown()之後,再向執行緒池提交任務的時候,如果你配置的拒絕策略是ThreadPoolExecutor.AbortPolicy的話,這個異常就被會丟擲來。2. 當你的排隊策略為有界佇列,並且配置的拒絕策略是ThreadPoolExecutor.AbortPolicy,當執行緒池的執行緒數量已經達到了maximumPoolSize的時候,你再向它提交任務,就會丟擲ThreadPoolExecutor.AbortPolicy異常。

顯示關閉掉執行緒池

這一點很好理解。比如說,你向一個倉庫去存放貨物,一開始,倉庫管理員把門給你打開了,你放了第一件商品到倉庫裡,但是當你放好出去後,有人把倉庫門關了,那你下次再來存放物品時,你就會被拒絕。示例程式碼如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class TextExecutor {
	public ExecutorService fixedExecutorService = Executors.newFixedThreadPool(5);
	public ExecutorService cachedExecutorService = Executors.newCachedThreadPool();
	public ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
	
	public void testExecutorException() {
		for (int i = 0; i < 10; i ++) {
			fixedExecutorService.execute(new SayHelloRunnable());
			fixedExecutorService.shutdown();
		}
	}
	
	private class SayHelloRunnable implements Runnable {

		@Override
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				System.out.println("hello world!");
			}
			
		}
	}
	
	public static void main(String[] args) {
		TextExecutor testExecutor = new TextExecutor();
		testExecutor.testExecutorException();
	}
}

解決方案

1. 不要顯示的呼叫shutdown方法,例如Android裡,只有你在Destory方法裡cancel掉AsyncTask,則執行緒池裡沒有活躍執行緒會自己回收自己。2. 呼叫執行緒池時,判斷是否已經shutdown,通過API方法isShutDown方法判斷,示例程式碼:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class TextExecutor {
	public ExecutorService fixedExecutorService = Executors.newFixedThreadPool(5);
	public ExecutorService cachedExecutorService = Executors.newCachedThreadPool();
	public ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
	
	public void testExecutorException() {
		for (int i = 0; i < 10; i ++) {
			// 增加isShutdown()判斷
			if (!fixedExecutorService.isShutdown()) {
				fixedExecutorService.execute(new SayHelloRunnable());
			}
			fixedExecutorService.shutdown();
		}
	}
	
	private class SayHelloRunnable implements Runnable {

		@Override
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				System.out.println("hello world!");
			}
			
		}
	}
	
	public static void main(String[] args) {
		TextExecutor testExecutor = new TextExecutor();
		testExecutor.testExecutorException();
	}
}

執行緒數量超過maximumPoolSize

示例程式碼裡使用了自定義的ExecutorService,可以復現這種問題:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class TextExecutor {
	public ExecutorService fixedExecutorService = Executors.newFixedThreadPool(5);
	public ExecutorService cachedExecutorService = Executors.newCachedThreadPool();
	public ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
	public ExecutorService customerExecutorService = new ThreadPoolExecutor(3, 5, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
	
	public void testExecutorException() {
		for (int i = 0; i < 10; i ++) {
			// 增加isShutdown()判斷
			if (!fixedExecutorService.isShutdown()) {
				fixedExecutorService.execute(new SayHelloRunnable());
			}
			fixedExecutorService.shutdown();
		}
	}
	
	public void testCustomerExecutorException() {
		for (int i = 0; i < 100; i ++) {
			customerExecutorService.execute(new SayHelloRunnable());
		}
	}
	
	private class SayHelloRunnable implements Runnable {

		@Override
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				System.out.println("hello world!");
			}
			
		}
	}
	
	public static void main(String[] args) {
		TextExecutor testExecutor = new TextExecutor();
		testExecutor.testCustomerExecutorException();;
	}
}

解決方案

1. 儘量調大maximumPoolSize,例如設定為Integer.MAX_VALUE
	public ExecutorService customerExecutorService = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
2. 使用其他排隊策略,例如LinkedBlockingQueue
<span style="white-space:pre">	</span>public ExecutorService customerExecutorService = new ThreadPoolExecutor(3, 5, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());




相關推薦

解決java.util.concurrent.RejectedExecutionException

前言昨晚12:00執行自動化測試指令碼時遇到了java.util.concurrent.RejectedExecutionException這個異常,從異常名稱裡很容易分析出是提交的任務被執行緒池拒絕了。檢視原始碼發現是在Activity裡,AsyncTask是在自定義的執行

Android解決Java.util.concurrent.ExecutionException: com.Android.ide.common.process.ProcessException:異常

錯誤: Error:Execution failed for task ':app:mergeDebugResources'. > Error: java.util.concurrent.ExecutionException: com.android.ide.common.process.P

java.util.concurrent.RejectedExecutionException異常發生的時機

概述線上程池的使用過程中,我們有時候會遇到下面的異常那麼什麼時候會發生這個異常呢1.執行緒池關閉以後,再次提交任務2.提交執行緒的數量大於最大執行緒數+任務佇列中排隊的個數接下來我們模擬一下情況一 ExecutorService service = Execut

出現java.util.concurrent.RejectedExecutionException或者Thread pool exhausted怎麼辦?

RejectedExecutionException表示執行緒池已經達到最大值,並且沒有空閒連,拒絕執行了一些任務。 Thread pool exhausted通常是min和max不一樣大時,表示當前已建立的連線用完,進行了一次擴充,建立了新執行緒,但不影響執行。 原因可

java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleExcept問題解決方案

log blank article 問題解決 安裝路徑 model row spa exec 在部署Dynamic Web Project時,如果正確配置web.xml或者標註時,仍然出現以上異常的話,可以嘗試以下內容講解的方法:   首先,雙擊eclipse中的serve

Android Studio出現java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException的總結和解決方法

logo ide roc for deb execution 所有 不同類 util 1. Error:Execution failed for task ‘mergeDebugAndroidTestResources‘.  > Error: java.util.c

java.util.concurrent.ExecutionException 錯誤解決

錯誤: java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].

spring cloud java.util.concurrent.TimeoutException的三種解決方式

spring cloud java.util.concurrent.TimeoutException解決方式 設定熔斷器檢測時間(預設1秒) hystrix.command.default.exec

解決錯誤: java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException

錯誤:Error:Execution failed for task ':app:transformClassesWithDexForReDebug'.>com.android.build.api.transform.TransformException:com.and

分享以下編譯錯誤:java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException的一種解決方法

最近一個專案從eclipse轉為了AS專案,修改依賴後第一次執行時碰到如下錯誤: Error:Execution failed for task ':videoPlayer:transformClassesWithDexForDebug'. > com.android

簡述synchronized和java.util.concurrent.locks.Lock異同

pda 阻塞 例如 好的 nbsp 釋放 非阻塞 ant 方法 主要相同點:Lock能完成synchronized所實現的所有功能。主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。syncronized會自動釋放鎖,而Lock一定要程序員手動釋

聊聊高並發(二十四)解析java.util.concurrent各個組件(六) 深入理解AQS(四)

sar 成功 通知 ati help write ng- ads 同步 近期總體過了下AQS的結構。也在網上看了一些講AQS的文章,大部分的文章都是泛泛而談。又一次看了下AQS的代碼,把一些新的要點拿出來說一說。 AQS是一個管程。提供了一個主要的同步器的

本地啟動tomcat的時候報java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: PermGen space

分析 man collect blog app 實例 heap bat linux系統 1、問題:我在tomcat中放入了大量的war包,啟動的時候報:java.util.concurrent.ExecutionException: java.lang.OutOfMemor

java.util.concurrent CyclicBarrier類

實現 .cn tint style pub interrupt com err rup CyclicBarrier類:   原文:一個同步輔助類,它允許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的線程的程序中,

本地啟動tomcat的時候報內存溢出錯誤:java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: PermGen space

技術分享 exce info compile 界面 current msi lang ech 問題分析:   PermGen space的全稱是Permanent Generation space,是指內存的永久保存區域,這塊內存主要是被JVM存放Class和Meta信息的

Java學習筆記—多線程(java.util.concurrent並發包概括,轉載)

java學習 read 可見性 locks body bsp wait lock ble 一、描述線程的類:Runable和Thread都屬於java.lang包 二、內置鎖synchronized屬於jvm關鍵字,內置條件隊列操作接口Object.wait()/notif

Java學習筆記—多線程(java.util.concurrent.locks包,轉載)

www 直接 一定的 比較 得到 非公平鎖 關於 無法 returns 一.synchronized的缺陷   synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。那麽為什麽會出現Lock呢?   如果一個代碼塊被synchronized修飾

Java學習筆記—多線程(原子類,java.util.concurrent.atomic包,轉載)

支持 位置 dset 賦值 嵌入 imp ans 匯編指令 sta 原子類 Java從JDK 1.5開始提供了java.util.concurrent.atomic包(以下簡稱Atomic包),這個包中 的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個變量的方

Java學習筆記—多線程(並發工具類,java.util.concurrent.atomic包)

配對 初始 訪問 接收 iter nco .get 執行 string 在JDK的並發包裏提供了幾個非常有用的並發工具類。CountDownLatch、CyclicBarrier和Semaphore工具類提供了一種並發流程控制的手段,Exchanger工具類則提供了在線程間

Java基礎知識-java.util.concurrent包下常見類的使用

finall iss con value 通信 out 否則 app ted 一,Condition 一個場景,兩個線程數數,同時啟動兩個線程,線程A數1、2、3,然後線程B數4、5、6,最後線程A數7、8、9,程序結束,這涉及到線程之間的通信。 public class