最近學習了線程過后,又想學學線程池,在寫測試代碼的時候想到一個問題,線程太多可能會導致內存占滿的問題,那線程池要不要關閉呢?怎么關閉呢?
已知關閉有兩種方法,shutdown()和shutdownNow()。shutdown()方法會關閉線程池,不再接受新的任務,已接受的任務會繼續執行,直到完成。shutdownNow()方法也類似,不過它會去嘗試終止正在執行的任務。如果任務都已提交或者都執行完,當然shutdown就沒問題啦。那還有線程沒execute線程池就被shutdown呢?
我們先寫一個線程池的公用單例。
/** * @ClassName ThreadPoolSingleTest * @Description 線程池單例模式 * @Auther zhui * @Date 2020/7/2 8:47 * @Version 1.0 **/ public class ThreadPoolSingleTest { private static ThreadPoolExecutor threadPoolExecutor=null; public static ThreadPoolExecutor getThreadPoolExecutor(){ if(threadPoolExecutor==null){ synchronized (ThreadPoolSingleTest.class){ if(threadPoolExecutor==null){ threadPoolExecutor= new ThreadPoolExecutor(10,50,5, TimeUnit.MINUTES,new LinkedBlockingQueue<>()); } } } return threadPoolExecutor; } }
然后測試如果還未execute,線程池就被別的線程shutdown了怎么辦?
public class Test { public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = ThreadPoolSingleTest.getThreadPoolExecutor(); //這個線程執行完成后就shutdown線程 threadPoolExecutor.execute(()->{ try{ Thread.sleep(2000); }catch (Exception e){} }); //模擬其他線程拿到了線程池實例,但是還未提交任務 new Thread(()->{ ThreadPoolExecutor threadPoolExecutor1 = ThreadPoolSingleTest.getThreadPoolExecutor(); out.println("new Thread已經拿到了ThreadPoolExecutor"); try{ //等它睡醒,懵逼的發現線程池被shutdown了 Thread.sleep(5000); }catch (Exception e){} threadPoolExecutor1.execute(()->{ out.println("執行"); }); }).start(); threadPoolExecutor.shutdown(); } }
運行結果還是報錯了,線程池被關掉了,無法提交任務。
Exception in thread "Thread-0" java.util.concurrent.RejectedExecutionException: Task 測試.Test$$Lambda$3/1588496549@50697d00 rejected from java.util.concurrent.ThreadPoolExecutor@5456e3d5[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) at 測試.Test.lambda$main$2(Test.java:310) at java.lang.Thread.run(Thread.java:748)
做到這里,就知道了,公用線程池是不能被shutdown的,畢竟在業務上,一個地方手賤的shutdown了,其他持有線程池對象卻沒來得及提交任務的代碼就GameOver了。但是但是就真的有人手賤要shutdown怎么辦?哎,那沒辦法,單例模式再多加一個判斷吧。
public class ThreadPoolSingleTest { private static ThreadPoolExecutor threadPoolExecutor=null; public static ThreadPoolExecutor getThreadPoolExecutor(){ //為null和被shutdown都實例化對象 if(threadPoolExecutor==null||threadPoolExecutor.isShutdown()){ synchronized (ThreadPoolSingleTest.class){ if(threadPoolExecutor==null||threadPoolExecutor.isShutdown()){ threadPoolExecutor= new ThreadPoolExecutor(10,50,5, TimeUnit.MINUTES,new LinkedBlockingQueue<>()); } } } return threadPoolExecutor; } }
以上方法只是防止被shutdown后執行任務失敗,但是還是會有錯誤的風險,所以最好還是不要隨便的shutdown線程池了。