最近学习了线程过后,又想学学线程池,在写测试代码的时候想到一个问题,线程太多可能会导致内存占满的问题,那线程池要不要关闭呢?怎么关闭呢?
已知关闭有两种方法,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线程池了。