(四)java 线程,线程池的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ganfanzhou/article/details/80722020

为什么要使用线程池?

   创建线程是简单的,但启动后的线程犹如脱缰野马,难于管理,特别是多线程使用场景,线程之间的互相竞争,可能使 cpu 花费更多时间在各个线程之间切换,而且线程结束后的回收由垃圾回收控制,你不知道工作结束的线程还会存活多久,是否持有着什么资源。而且线程对象提供使用方法有限,无法提供定时启动、线程并发数控制等操作,所以,线程池出现了。
   线程池提供了对线程的管理,让线程稍微能得到控制(中断、取消等);并发线程数的控制,提高系统资源使用率,避免过多的资源竞争;复用线程,减少线程对象创建,提高线程启动效率以及降低系统资源消耗;提供延时启动、定期执行等操作。

线程池介绍

具体线程池有以下几个:

  • ThreadPoolExecutor
    继承 AbstractExecutorServiceExecutors 提供的默认线程池,除了定时线程池,其他都是ThreadPoolExecutor的默认参数构建
  • ScheduledThreadPoolExecutor可调度线程池(定时线程池)
    继承 ThreadPoolExecutor实现ScheduledExecutorService
  • ForkJoinPool ,继承 AbstractExecutorService
    特殊线程池,分治法(Divide-and-Conquer Algorithm),可以将一个大任务(线程)在细分多个小任务执行,直到所有小任务完成才算大任务完成,充分利用多核 cpu 的特性。与 ThreadPoolExecutor 区别,ForkJoinPool可以实现在有限的线程数下完成非常多的任务,如使用4个线程完成具有父子关系的400万个任务,而 ThreadPoolExecutor 无法选择优先执行子任务,所以处理400万的任务,需要400万个线程。

  • ExecutorCompletionService
    实现 CompletionService,构造方法传入 Executor,可以认为是对 Executor 的使用封装。

使用较多的线程池是 ThreadPoolExecutor,本文只介绍这个。对其他类型有兴趣的可以去 java.util.concurrent 中查看。

构造方法:

public ThreadPoolExecutor(int corePoolSize,//核心池数量,并发的线程数
                          int maximumPoolSize,//线程池最大容量
                          long keepAliveTime,//线程存活时间
                          TimeUnit unit,//时间单位
                          BlockingQueue<Runnable> workQueue,//线程缓存队列
                          ThreadFactory threadFactory,//线程创建工厂
                          RejectedExecutionHandler handler//加入线程失败策略) {
                              //... 省略

}

向线程池提交一个任务,如果核心池未满,则用该命令作为第一个任务开启一个新线程

if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))//true:添加成功 false:添加失败
        return;
    c = ctl.get();
}

如果核心池已经满了,检查线程池最大容量,没有超过,则加入缓存工作队列,创建新线程去执行任务;当线程池达到最大容量,并且缓存队列已经饱和时,此时再继续往线程池中添加任务,则会拒绝。
由缓存队列创建的线程,如果处于空闲状态,在一定的时间(keepAliveTime)后将会被回收,而核心池中的线程如果 allowCoreThreadTimeOut 该参数为 false(默认值) 即使处于空闲状态也不会被回收。

流程图如下:

Created with Raphaël 2.1.2 添加任务 核心池已满? 加入工作队列 达到最大线程数? 超出工作队列容量? 移出工作队列 拒绝 结束 添加成功 创建线程 yes no yes no yes no

使用:

ThreadPoolExecutor pool = new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
pool.execute(()-> print "runnable");

一般来说,除非有特殊的处理要求,否则建议使用Executors 中的方法构造线程池,对于线程池的核心数、最大容量、工作队列结构、线程工厂等设置,默认构造基本满足开发需求。

关闭线程池:

//不再接受新任务,直到缓存区任务执行完毕
public void shutdown(){...}
//不再接受新任务,将缓存区任务返回,并清空工作区,中断运行中的任务
public List<Runnable> shutdownNow() {...}

猜你喜欢

转载自blog.csdn.net/ganfanzhou/article/details/80722020