多线程之线程池(上)

前言

最近一段时间,咱们一直都在分享多线程相关知识,也一直有用线程池,可是一直没有介绍线程池相关知识,因此今天咱们就先来看下线程池相关的知识点。缓存

线程池

ThreadPool,线程池,顾名思义就是存放线程的池子,也是jdk1.5引入的。对咱们而言,它的最主要优点就是简化了线程启动流程,让咱们能够更方便地使用多线程,不再用手动start线程,直接经过线程池提交咱们的任务便可,并且合理使用线程池至于能够带来如下几个好处:多线程

  • 下降资源消耗:复用线程,下降建立和销毁线程带来的资源消耗ide

  • 提升响应速度:使用线程池,省去了线程建立和初始化过程,因此任务能够更快执行线程

  • 提升线程的可管理性:能够直接经过线程池管理、监控、调度线程,线程管理更方便debug

经常使用线程池

经常使用的线程池有SingleThreadExecutor、CachedThreadPool、ScheduledThreadPool、FixedThreadPool,他们分别是单线程调度器,缓存线程池,定时任务线程池和固定线程池,他们均可以经过Executors建立,调用对应的静态方法便可,因为这一块的内容比较多,因此今天就简单提一下,后面专门讲一次。3d

自定义线程池

咱们今天着重讲下自定义线程池,自定义线程池也很简单,直接new ThreadPoolExecutor() ,而后传入对应的参数便可,你们能够看下下面的示例:blog

int corePoolSize = 10;
int maximumPoolSize = 20;
long keepAliveTime = 1000;
TimeUnit unit = TimeUnit.MICROSECONDS;
BlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
for (int i = 0; i < 50; i++) {
    threadPoolExecutor.execute(() -> {
        String name = Thread.currentThread().getName();
        System.out.println("hello threadPool: "+ name);
    });
}
threadPoolExecutor.shutdown();

ThreadPoolExecutor有三个构造方法,至少须要三个参数队列

其中第一个参数是线程池的基本大小,当你提交一个任务到线程池时,线程池会建立一个线程来执行任务,即便又空闲的线程存在,线程池依然会启动一个新线程来执行当前任务,直到线程池中的线程数达到线程基本大小(corePoolSize);资源

第二个参数是线程池容许建立的最大线程数。若是工做队列(第三个参数)满了,且建立线程数已达到线程池基本大小,则线程池会继续建立新的线程来执行任务。若是你指定的工做队列是***的,那这个参数也就失效了。get

第三个参数就是线程池工做队列,就是当你须要执行的任务超过线程池基本大小的时候,会把超出部分放进工做队列,等待线程池基本线程资源释放。

下面咱们分别验证以上三点,运行上面的示例代码:

在第一次循环的时候(i=0),咱们发现线程池的size是0,活动线程数也是0,任务队列也是0,完成任务数也是0,这也说明线程池在最开始的时候是没有建立线程的;

而后咱们让他循环到第9次(i=8),这时候线程池已经被初始化,有8个线程(因为断点的缘由,第9个线程还没有被建立),活动线程数5,完成执行的任务数3,任务队列仍是0,说明确实在未达到线程池基本大小时,会不断建立新的线程;

咱们继续执行,让他循环到第15次(i=14),可结果彷佛和咱们预期不同,按照预期,线程池的size应该是10,活动线程数也是10,任务队列也是4,完成任务数可能不肯定,因此这里确定不能经过debug的方式来看了,由于debug停顿以后好多线程资源已经被释放,任务根本就不会堆积,因此任务队列就不会有数据:

因此这里我在线程池启动任务前加一行打印,打印线程池数据

而后在运行,就能够拿到运行数据:

这样的数据才是真实的,由于实际运行的时候,线程启动是很是快的,因此执行完成的任务数应该是0,等待任务数是4。

咱们前面设定的最大线程数是20,可是翻看运行记录,我发现线程池的大小始终是10,说明只要不打到任务队列的上限,并不会建立新的线程,这里咱们把循环次数改成60,而后运行下:

可是依然没有建立新的线程,由于仍是内没有达到任务队列上限,咱们把循环次数再调大一点,调到70:

如今线程池的大小就变成了19,活动线程数19,可是这时候若是你继续调大循环次数,线程池就会报错了:

这个错误的缘由就是线程池资源已经耗尽了,没法再接收新的任务了,这也就是说线程池可以处理的最大任务数是corePoolSize + maximumPoolSize + workQueue.size() ,固然,若是你的workQueue不设定大小,那永远都不会报这个错误,固然maximumPoolSize 也就无效了。

总结

本来打算线程池一次分享完的,可是实际分享过程当中发现内容太多了(已经一千五百字了),因此今天就先到这里,明天再继续分享线程池其余内容。总的来讲,今天的内容已经说明白了线程池不少基础的知识点(反正我本身以为我都有好多收获),算是干货满满吧,你若是掌握了这些知识点,至少在使用线程池的过程当中会少踩好多坑。好了,今天就先说这么多吧!

相关文章
相关标签/搜索