最近在项目过程当中,有了一些对线程池使用的经验。线程
1.如何等待线程池中的所有任务执行完设计
能够经过ExecutorService的awaitTermination方法。在调用线程池的shutdown()方法后,再调用线程池的awaitTermination()方法,此方法会一直阻塞设置的时间,时间到后,若是线程池中的任务都已经执行完,返回true,否者返回false。利用while循环能够将让程序在线程池中的worker都执行完再执行后续的代码。队列
ExecutorService pool = ThreadPool.getLanguagePool(); pool.shutdown(); try { //每隔20秒检查一次任务是否所有执行完 while (!pool.awaitTermination(WAIT_TIME, TimeUnit.SECONDS)) { System.out.println("程序执行耗时" + ((System.currentTimeMillis() - start) / 1000) + "秒,还未结束"); } } catch (InterruptedException e) { System.out.println("任务被中断"); e.printStackTrace(); }
2.如何判断线程池中的worker都执行完get
简单的需求中能够在关闭线程池后,经过awaitTermination()方法等待。但若是主任务会产生一些子任务,而后将子任务提交到同一个线程池,则必须等到池中的所有主任务都执行完,才能调用shutdown()方法,不然,在线程池处于拒绝新任务状态,还在执行中的主任务一旦提交了子任务,将抛出RejectedExecutionException。同步
对于cached线程池,由于workers队列中的thread在没有新任务产生60秒后,thread就会被回收销毁,因此能够经过获取workers队列大小来判断池中是否还有任务,任务所有执行完,就不存在主任务再提交子任务的状况了。it
ThreadPoolExecutor pe = (ThreadPoolExecutor) pool; System.out.println("工做线程池大小:" + pe.getPoolSize()); while (pe.getPoolSize() != 0) { System.out.println("工做线程池,如今大小为:" + pe.getPoolSize()); //等待cacheThreadPool中的线程所有执行完成 threadSleep(); } System.out.println("工做线程池,任务所有执行完毕,能够关闭"); pool.shutdown();
对于fixed线程池,由于它在task队列为空,剩余任务数量小于coreSize大小后,不会将thread回收,将workers数量降到coreSize如下,所以getPoolSize返回值一直不会为0。对此能够采用的方法是,设置一个全局的AtomicInteger计数器,在建立一个主任务时+1,主任务执行完毕-1,而后利用while循环和主线程sleep,能够一直检查主任务是否都执行结束,都结束后,再关闭线程池(还有其余方法最后会提到)。io
其实,能够将子任务提交到其余的线程池。这样当主任务都提交后,就能够关闭线程池了。thread
3.对自定义线程池的考虑List
在项目中,我发现有些任务若是提交到cached线程池,任务太多时由于每一个任务的执行时间都很短,将减低程序吞吐量。若是提交到fixed线程池,当这些任务执行完毕后,将有coreSize数量的thread不能被回收,它们一直在阻塞队列的take()上等待新任务被提交到线程池,一直处于WAITING状态。为了求到一个平衡,须要用自定义线程池。线程池
int corePoolSize, 线程核心池大小,当线程数量达到此值后,将不会减少,为了能回收全部线程,将其设置为0;
int maximumPoolSize,最大线程数量,为了保证每一个task都有足够的cpu时间,须要控制此最大值。这个值的设计须要考虑任务中cpu时间和其余阻塞时间的比值,阻塞时间越多,就可让更多的线程来执行任务,避免task线程都在阻塞,浪费cpu时间。个人电脑是至强cpu,支持32个线程执行,我设置值是32 * 3。
long keepAliveTime,线程空闲多少时间后回收线程,可使用cached池60秒的设计。
TimeUnit unit,空闲时间的单位。
BlockingQueue<Runnable> workQueue,任务队列的类型,fixed池用的是底层为linkedList的无界阻塞队列。cached池用的是同步阻塞队列,提交任务的线程,必须等到有worker线程取走任务才能返回,获取任务的线程,也必须等待有线程提交了任务才能取走返回,因此这个池实际大小为0。我用的是fixed池的LinkedBlockingQueue。
ThreadFactory,线程工厂,实现它主要是为了设置线程的name,这样看线程栈要方便不少。
最后,个人线程池以下
new ThreadPoolExecutor(MINI_THREAD_SIZE/*0*/, MAX_THREAD_SIZE/*96*/, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new TaskFactory("mail"));
其实,coreSize能够不设置为0,经过调用线程池的
allowCoreThreadTimeOut(boolean value)
方法,能够设置是否容许线程超时后被回收(默认是false),这样当核心池一直空闲时,核心池线程也能被回收。