线程池的学习
为什么会出现线程池?
在实际业务中使用多线程去处理业务,主要是为了将资源利用最大化,从而提高效率。但是我们上一节讲到了线程的创建与销毁,若频繁的创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。此时我们会想有没有线程可以进行复用,就是创建一次可以在多个任务中使用,并不被销毁操作?
通过API,我们可以知道满足以上条件的正是本章节需要学习的ThreadPoolExecutor类。
了解ThreadPoolExecutor
通过查看源码,发现ThreadPoolExecutor类,有七个参数支撑,首先了解一下这七个参数:
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
线程是否启动?
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。即:超出corePoolSize的线程数,进行等待执行线程的等待时间,超出这个时间就会终止这个线程。
unit:参数keepAliveTime的时间单位,在TimeUnit类中有7种静态属性,如下:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,参考网上给出的策略如下所示:
ArrayBlockingQueue; // 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue; // 一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
SynchronousQueue; // 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue; // 静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue; // 一个具有优先级的无限阻塞队列。
一般情况下,会默认使用LinkedBlockingQueue。
threadFactory:线程工厂,主要用来创建线程。我会默认填写Executors.defaultThreadFactory()。
handler:表示当拒绝处理任务时的策略,有四种取值,参考网上如下所示:
ThreadPoolExecutor.AbortPolicy; // 丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy; // 也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy; // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy; // 由调用线程处理该任务
线程池的主要操作方法如下所示:
execute:只能接受Runnable类型的任务
submit:不管是Runnable还是Callable类型的任务都可以接受,但是Runnable返回值均为void,所以使用Future的get()获得的还是null
shutdown:关闭线程池
shutdownNow:立即关闭线程池
Executors四种线程池
前面我们刚刚学习了ThreadPoolExecutor线程池类,是最底层的线程池的构造。通过源码可知道,Executors使用的线程池,底层都是ThreadPoolExecutor类。
如下图所示:
Executors.newCachedThreadPool()
解释:
可伸缩,根据CPU调整线程数量,遇强则强,遇弱则弱
Executors.newSingleThreadExecutor()
解释:
单个线程
Executors.newFixedThreadPool(int nThreads )
解释:
创建一个固定的线程池大小
Executors.newScheduledThreadPool(int corePoolSize)
解释:
是一个可以周期性执行任务的线程池。通俗的讲,就是指定时间执行任务!
举例说明周期性执行的线程池:
public static void main(String[] args) {
SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
System.out.println(simpleFormatter.format(new Date()));
pool.schedule(()->{
//定时任务
System.out.println("五秒后执行!" + simpleFormatter.format(new Date()));
}, 5, TimeUnit.SECONDS);
pool.shutdown();
}
执行结果如下: