对线程池使用的一些探索

最近在项目过程中,有了一些对线程池使用的经验。

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都执行完

简单的需求中可以在关闭线程池后,通过awaitTermination()方法等待。但如果主任务会产生一些子任务,然后将子任务提交到同一个线程池,则必须等到池中的全部主任务都执行完,才能调用shutdown()方法,否则,在线程池处于拒绝新任务状态,还在执行中的主任务一旦提交了子任务,将抛出RejectedExecutionException。

对于cached线程池,因为workers队列中的thread在没有新任务产生60秒后,thread就会被回收销毁,所以可以通过获取workers队列大小来判断池中是否还有任务,任务全部执行完,就不存在主任务再提交子任务的情况了。

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,可以一直检查主任务是否都执行结束,都结束后,再关闭线程池(还有其他方法最后会提到)。

其实,可以将子任务提交到其他的线程池。这样当主任务都提交后,就可以关闭线程池了。

3.对自定义线程池的考虑

在项目中,我发现有些任务如果提交到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),这样当核心池一直空闲时,核心池线程也能被回收。

转载于:https://my.oschina.net/u/588009/blog/790426

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值