Java线程池线程池ThreadPoolExecutor原理及使用实例原理及使用实例
主要介绍了Java线程池ThreadPoolExecutor原理及使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以
参考下
引导引导
要求:线程资源必须通过线程池提供,不允许在应用自行显式创建线程;
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗内存或
者“过度切换”的问题。
线程池介绍线程池概述线程池介绍线程池概述
线程池,顾名思义是一个放着线程的池子,这个池子的线程主要是用来执行任务的。当用户提交任务时,线程池会创建线程去执行任务,若任务超过了核心线程数的时候,
会在一个任务队列里进行排队等待,这个详细流程,我们会后面细讲。
任务,通常是一些抽象的且离散的工作单元,我们会把应用程序的工作分解到多个任务中去执行。一般我们需要使用多线程执行任务的时候,这些任务最好都是相互独立
的,这样有一定的任务边界供程序把控。
多线程,当使用多线程的时候,任务处理过程就可以从主线程中剥离出来,任务可以并行处理,同时处理多个请求。当然了,任务处理代码必须是线程安全的。
为何要使用线程池?为何要使用线程池?
降低开销:在创建和销毁线程的时候会产生很大的系统开销,频繁创建/销毁意味着CPU资源的频繁切换和占用,线程是属于稀缺资源,不可以频繁的创建。假设创建线程的时长记为
t1,线程执行任务的时长记为t2,销毁线程的时长记为t3,如果我们执行任务t2<t1+t3,那么这样的开销是不划算的,不使用线程池去避免创建和销毁的开销,将是极大的资源浪费。
易复用和管理:将线程都放在一个池子里,便于统一管理(可以延时执行,可以统一命名线程名称等),同时,也便于任务进行复用。
解耦:将线程的创建和销毁与执行任务完全分离出来,这样方便于我们进行维护,也让我们更专注于业务开发。线程池的优势提高资源的利用性:通过池化可以重复利用已创建的线
程,空闲线程可以处理新提交的任务,从而降低了创建和销毁线程的资源开销。提高线程的管理性:在一个线程池中管理执行任务的线程,对线程可以进行统一的创建、销毁以及监控
等,对线程数做控制,防止线程的无限制创建,避免线程数量的急剧上升而导致CPU过度调度等问题,从而更合理的分配和使用内核资源。提高程序的响应性:提交任务后,有空闲线
程可以直接去执行任务,无需新建。提高系统的可扩展性:利用线程池可以更好的扩展一些功能,比如定时线程池可以实现系统的定时任务。线程池原理线程池的参数类型
一共有7个:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler,(5+2,前5个重要)
int corePoolSize:该线程池中核心线程数最大值
这边我们区分两个概念:
核心线程:线程池新建线程的时候,当前线程总数< corePoolSize,新建的线程即为核心线程。非核心线程:线程池新建线程的时候,当前线程总数< corePoolSize,新建的线程即为
核心线程。
核心线程默认情况下会一直存活在线程池中,即使这个核心线程不工作(空闲状态),除非ThreadPoolExecutor 的 allowCoreThreadTimeOut这个属性为 true,那么核心线程如果空闲状
态下,超过一定时间后就被销毁。
int maximumPoolSize:线程总数最大值
线程总数 = 核心线程数 + 非核心线程数
long keepAliveTime:非核心线程空闲超时时间
keepAliveTime即为空闲线程允许的最大的存活时间。如果一个非核心线程空闲状态的时长超过keepAliveTime了,就会被销毁掉。注意:如果设置allowCoreThreadTimeOut =
true,就变成核心线程超时销毁了。
TimeUnit unit:是keepAliveTime 的单位
TimeUnit 是一个枚举类型,列举如下:
单位
单位单位 说明说明
NANOSECONDS
1微毫秒 = 1微秒 /
1000
MICROSECONDS 1微秒 = 1毫秒 / 1000
MILLISECONDS 1毫秒 = 1秒 /1000
SECONDS 秒
MINUTES 分
HOURS 小时
DAYS 天
BlockingQueue workQueue:存放任务的阻塞队列:存放任务的阻塞队列
当核心线程都在工作的时候,新提交的任务就会被添加到这个工作阻塞队列中进行排队等待;如果阻塞队列也满了,线程池就新建非核心线程去执行任务。workQueue维护的是等
待执行的Runnable对象。
常用的 workQueue 类型:(无界队列、有界队列、同步移交队列)
SynchronousQueue:同步移交队列,适用于非常大的或者无界的线程池,可以避免任务排队,SynchronousQueue队列接收到任务后,会直接将任务从生产者移交给工作者线程,这
种移交机制高效。它是一种不存储元素的队列,任务不会先放到队列中去等线程来取,而是直接移交给执行的线程。只有当线程池是无界的或可以拒绝任务的时
候,SynchronousQueue队列的使用才有意义,maximumPoolSize 一般指定成 Integer.MAX_VALUE,即无限大。要将一个元素放入SynchronousQueue,就需要有另一个线程在等待
接收这个元素。若没有线程在等待,并且线程池的当前线程数小于最大值,则ThreadPoolExecutor就会新建一个线程;否则,根据饱和策略,拒绝任务。newCachedThreadPool默认
使用的就是这种同步移交队列。吞吐量高于LinkedBlockingQueue。
LinkedBlockingQueue:基于链表结构的阻塞队列,FIFO原则排序。当任务提交过来,若当前线程数小于corePoolSize核心线程数,则线程池新建核心线程去执行任务;若当前线程数
等于corePoolSize核心线程数,则进入工作队列进行等待。LinkedBlockingQueue队列没有最大值限制,只要任务数超过核心线程数,都会被添加到队列中,这就会导致总线程数永远
不会超过 corePoolSize,所以maximumPoolSize 是一个无效设定。newFixedThreadPool和newSingleThreadPool默认是使用的是无界LinkedBlockingQueue队列。吞吐量高于
ArrayBlockingQueue。
ArrayBlockingQueue:基于数组结构的有界阻塞队列,可以设置队列上限值,FIFO原则排序。当任务提交时,若当前线程小于corePoolSize核心线程数,则新建核心线程执行任务;
若当先线程数等于corePoolSize核心线程数,则进入队列排队等候;若队列的任务数也排满了,则新建非核心线程执行任务;若队列满了且总线程数达到了maximumPoolSize最大线程
数,则根据饱和策略进行任务的拒绝。
DelayQueue:延迟队列,队列内的元素必须实现 Delayed 接口。当任务提交时,入队列后只有达到指定的延时时间,才会执行任务
PriorityBlockingQueue:优先级阻塞队列,根据优先级执行任务,优先级是通过自然排序或者是Comparator定义实现。
注意: 只有当任务相互独立没有任何依赖的时候,线程池或工作队列设置有界是合理的;若任务之间存在依赖性,需要使用无界的
线程池,如newCachedThreadPool,否则有可能会导致死锁问题。
ThreadFactory threadFactory
创建线程的方式,这是一个接口,你 new 他的时候需要实现他的 Thread newThread(Runnable r) 方法,一般用不上,