首先传统的三种创建多线程的方式
继承Thread
实现Runnable接口
实现callable接口
代码如下:
1 | package ThreadPool; |
控制台显示:
那么到底什么是线程池:
创建线程时要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程有限,为了避免这些问题,在程序启动时就创建若干线程来响应处理,它们被称为线程池,里面的线程称为工作线程,从jdk1.5开始,JavaAPI提供了EXecutor框架让你可以创建不同的线程池。
话不多说上代码:
1 | package ThreadPool; |
上面那段代码是没用线程池实现将随机数放在ArrayList中,最后输出所用的时间和ArrayList的大小(size)如下图:
我们此时再将用了线程池的代码进行比对,代码如下:
1 | package ThreadPool; |
控制台输出信息:
可以看出时间短了很多(当然如果电脑处理器好的话就几十毫秒)
线程池的优点:
1、避免线程创建和销毁带来的开销
2、避免大量线程间因为互相抢占系统资源导致的阻塞现象
3、能对线程进行简单的管理并提供定时执行、间隔执行等功能
接下来正常普通线程池的创建:
1 | package ThreadPool; |
代码中分别用了submit和execute来输出111,那么两者有什么区别呢?
- execute没有返回值,如果不需要知道线程的结果就使用execute方法,性能会好很多。
- submit返回一个Future对象,如果想知道线程结果就使用submit提交,而且它能在主线程中通过Future的get方法捕获线程中的异常。
线程池核心类:
在java.util.concurrent包中我们能找到线程池的定义,其中ThreadPoolExecutor是我们线程池核心类,首先看看线程池类的主要参数有哪些。
1 |
|
- corePoolSize:线程池的核心大小,也可以理解为最小的线程池大小。
- maximumPoolSize:最大线程池大小。
- keepAliveTime:空余线程存活时间,指的是超过corePoolSize的空余线程达到多长时间才进行销毁。
- unit:销毁时间单位。
- workQueue:存储等待执行线程的工作队列。
- threadFactory:创建线程的工厂,一般用默认即可。
- handler:拒绝策略,当工作队列、线程池全已满时如何拒绝新任务,默认抛出异常。
线程池工作流程
1、如果线程池中的线程小于corePoolSize时就会创建新线程直接执行任务。
2、如果线程池中的线程大于corePoolSize时就会暂时把任务存储到工作队列workQueue中等待执行。
3、如果工作队列workQueue也满时,当线程数小于最大线程池数maximumPoolSize时就会创建新线程来处理,而线程数大于等于最大线程池数maximumPoolSize时就会执行拒绝策略。
一般流程图如下(要详细了解各类线程池流程图的可移步到 https://www.cnblogs.com/linguanh/p/8000063.html ):
线程池分类
Executors是jdk里面提供的创建线程池的工厂类,它默认提供了4种常用的线程池应用,而不必我们去重复构造。
newFixedThreadPool
固定线程池,核心线程数和最大线程数固定相等,而空闲存活时间为0毫秒,说明此参数也无意义,工作队列为最大为Integer.MAX_VALUE大小的阻塞队列。当执行任务时,如果线程都很忙,就会丢到工作队列等有空闲线程时再执行,队列满就执行默认的拒绝策略。
newCachedThreadPool
带缓冲线程池,从构造看核心线程数为0,最大线程数为Integer最大值大小,超过0个的空闲线程在60秒后销毁,SynchronousQueue这是一个直接提交的队列,意味着每个新任务都会有线程来执行,如果线程池有可用线程则执行任务,没有的话就创建一个来执行,线程池中的线程数不确定,一般建议执行速度较快较小的线程,不然这个最大线程池边界过大容易造成内存溢出。
newSingleThreadExecutor
单线程线程池,核心线程数和最大线程数均为1,空闲线程存活0毫秒同样无意思,意味着每次只执行一个线程,多余的先存储到工作队列,一个一个执行,保证了线程的顺序执行。
newScheduledThreadPool
调度线程池,即按一定的周期执行任务,即定时任务,对ThreadPoolExecutor进行了包装而已。
拒绝策略
AbortPolicy
简单粗暴,直接抛出拒绝异常,这也是默认的拒绝策略。CallerRunsPolicy
如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。DiscardPolicy
从方法看没做任务操作,即表示不处理新任务,即丢弃。DiscardOldestPolicy
抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。如何关闭线程池
1 | es.shutdown(); |
不再接受新的任务,之前提交的任务等执行结束再关闭线程池。
1 | es.shutdownNow(); |
不再接受新的任务,试图停止池中的任务再关闭线程池,返回所有未处理的线程list列表