什么是池?数据库
汉字的基本释义:数组
池塘;旁边高中间洼的地方等缓存
在网络技术范畴内,池(Pool)的概念被普遍的应用在服务器端软件的开发上。服务器
使用池结构相比于不使用池结构的基本优点分析网络
在衔接节点上,无池结构时须要不断的建立和销毁新服务对象必将给形成系统资源的巨大开销,致使系统的性能降低,甚至系统资源耗尽并发
使用池结构能够减小没必要要的新服务对象的建立和销毁,用一个容器保存着各类须要的对象。对这些对象进行复用,从而下降系统资源开销、提升程序的响应速度、改善效率。框架
经常使用的池技术有:Socket链接池、JDBC链接池、线程池等。性能
本质上池是在资源使用时将使用率高且能够复用的对象保存在一个相似容器的地方,对这些对象进行复用,从而下降非必须的系统资源开销、提升程序的响应速度、改善效率。ui
线程池剖析spa
线程池的生命周期及其使用
1.建立
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
milliseconds,runnableTaskQueue, handler);
corePoolSize(线程池的基本大小)提交一个任务到线程池时,线程池会建立一个线程来执行任务,即便其余空闲的基本线程可以执行新任务也会建立线程,等到须要执行的任务数大于线程池基本大小时就再也不建立。若是调用了线程池的prestartAllCoreThreads()方法,线程池会提早建立并启动全部基本线程。
maximumPoolSize(线程池最大数量):线程池容许建立的最大线程数。若是队列满了,而且已建立的线程数小于最大线程数,则线程池会再建立新的线程执行任务。值得注意的是,若是使用了无界的任务队列这个参数就没什么效果。
keepAliveTime(线程活动保持时间):线程池的工做线程空闲后,保持存活的时间。因此,若是任务不少,而且每一个任务执行的时间比较短,能够调大时间,提升线程的利用率。
TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)
runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。能够选择如下几个阻塞队列:
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量一般要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
SynchronousQueue:一个不存储元素的阻塞队列。每一个插入操做必须等到另外一个线程调用
移除操做,不然插入操做一直处于阻塞状态,吞吐量一般要高于Linked-BlockingQueue,默认为Integer.MAX_VALUE2147483647。静态工厂方法Executors.newCachedThreadPool使用了这个队列
PriorityBlockingQueue:一个具备优先级的无限阻塞队列
ThreadFactory:用于设置建立线程的工厂,能够经过线程工厂给每一个建立出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder能够快速给线程池里的线
程设置有意义的名字,代码以下
new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build()
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状
态,那么必须采起一种策略处理提交的新任务。这个策略默认状况下是AbortPolicy,表示没法
处理新任务时抛出异常。在JDK 1.5中Java线程池框架提供了如下4种策略。
·AbortPolicy:直接抛出异常。
·CallerRunsPolicy:只用调用者所在线程来运行任务。
·DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
·DiscardPolicy:不处理,丢弃掉。
固然,也能够根据应用场景须要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化存储不能处理的任务
2.提交
execute()用于提交不须要返回值的任务,因此没法判断任务是否被线程池执行成功。
submit()提交须要返回值的任务。线程池会返回一个future类型的对象,经过这个future对象能够判断任务是否执行成功,而且能够经过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后当即返回,这时候有可能任务没有执行完。
3.关闭
shutdown将线程池的状态设置成SHUTDOWN状态,而后中断全部没有正在执行任务的线程。
shutdownNow首先将线程池的状态设置成STOP,而后尝试中止全部的正在执行或暂停任务的线程,并返回等待执行任务的列表。
线程池的类型
newCachedThreadPool建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。队列-SynchronizedQueue存放大小1
newFixedThreadPool定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
队列-LinkedBlockingQueue-无界致使maxNumPoolSize无效
newScheduledThreadPool 建立一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。
合理使用线程池
从几个不一样角度来判断和分析
任务的性质:
CPU密集型任务-应配置尽量小的线程数量,如配置N*cpu+1个线程的线程池
【获取cpu核数:Runtime.getRuntime().availableProcessors()】
IO密集型任务-线程并非一直在执行任务,则应配置尽量多的线程
混合型任务-若是能够拆分,将其拆分红一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量
将高于串行执行的吞吐量。若是这两个任务执行时间相差太大,则不必进行分解。
能够经过Runtime.getRuntime().availableProcessors()方法得到当前设备的CPU个数
任务的优先级
可使用优先级队列PriorityBlockingQueue来处理。它可让优先级高的任务先执行。
执行时间不一样
能够交给不一样规模的线程池来处理,或者可使用优先级队列,让执行时间短的任务先执行。
依赖数据库链接池的任务,由于线程提交SQL后须要等待数据库返回结果,等待的时间越
长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。线程池的监控
线程池的实现原理
当一个任务提交时(excute())
1.线程池判断核心线程池里的线程是否都在执行任务,若不是(少于corePoolSize)则建立一个工做线程在执行任务(须要获取全局锁),若是已满,则进入下一步
2.线程池判断工做队列是否已满,若是没满则将任务线程添加入工做队列(BlockingQueue),不然进去下一步
3.判断线程池里的线程是否都处于工做状态,不是(小于maximumPoolSize)则建立新线程来完成任务(须要获取全局锁),不然交给饱和策略[RejectExcutionHandle.rejectedExcution()]来处理这个任务
这些步骤是为了不使用全局锁[多是一个严重的可伸缩瓶颈]
工做线程:线程池建立线程时,会将线程封装成工做线程worker,worker在执行任务完成以后会循环从阻塞队列里获取任务来执行。