通過對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