线程复用:线程池
为了避免系统频繁地创建和销毁线程,我们可以让创建的线程进行复用。线程池中,总有几个活跃线程。当你需要使用线程时,可以从池中随便获取一个空闲线程,当工作完成时,线程不会关闭而是退回池中。
JDK 提供的线程池工厂方法
newFixedThreadPool
返回固定线程数量的线程池。
当有一个新的任务提交时,线程池若有空闲线程,则立即执行。若没有,则新的任务会被暂存到一个任务队列中,待线程空闲时,便处理在任务队列中的任务。
newSingleThreadExecutor
返回只有一个线程的线程池。
若多余一个任务被提及,则保存到任务队列中,线程空闲时按照先入先出的顺序执行任务。
newCachedThreadPool
返回一个可根据实际情况调整线程数量的线程池。
线程池的线程数量不确定,但若有空闲线程可以复用,则会有限使用可复用的线程。若所有线程均在工作,又有新任务提交,则创建新的线程处理任务。当所有线程在任务执行完之后,将返回线程池进行复用。
newSingleThreadScheduledExecutor
返回一个
ScheduledExecutorService
对象,线程池大小为1。ScheduledExecutorService
接口在ExecutorService
接口之上扩展了给定时间执行某任务的功能。newScheduledThreadPool
返回指定线程数量的
ScheduledExecutorService
对象。
线程池的内部实现
JDK 提供的线程池工厂方法实质就是
ThreadPoolExecutor
的不同参数的不同实现public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize
: 指定线程池中的线程数量。maximumPoolSize
:指定线程池中的最大线程数量。keepAliveTime
:超过corePoolSize的空闲线程,在多长时间内,会被销毁。unit
:keepAliveTime的单位。workQueue
:提交但未执行的任务队列。直接提交的队列
该功能由
SynchronousQueue
对象提供,提交的任务不会被保存,而是直接提交给线程执行,如果没有空闲的线程,则尝试创建新的线程,如果已达到最大值则执行拒绝策略。有界的任务队列
由
ArrayblockingQueue
实现,ArrayblockingQueue
的构造函数需要带一个容量参数(队列最大容量)。当有新的任务时,如果线程池实际线程数小于corePoolSize
,则优先创新新的线程,如果大于corePoolSize
,则将任务加入等待队列。若等待队列已满,则在总线程数不大于
maximumPoolSize
的前提下创建新的线程执行任务,否则则执行拒绝策略。因为需要等待队列满才会创建超出corePoolSize
的线程,所以除非系统特别繁忙,否则核心线程数一般维持在corePoolSize
。无界的任务队列
由
LinkedBlockingQueue
实现,和有界的任务队列类似,只是等待队列会一直扩张,如果任务的创建速度一直高于处理速度,则无界队列会一直增长,直至耗尽内存。优先任务队列
由
PriorityBlockingQueue
实现,是一个特殊的无界队列,会按照任务自身的优先级顺序先后执行。
threadFactory
:线程工厂,用于创建线程,一般默认。handler
:拒绝策略。当任务太多时如何拒绝任务。AbortPolicy 策略
直接抛出异常,阻止系统正常工作。
CallerRunsPolicy 策略
只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。这样做不会真的丢弃任务,但是任务提交的线程性能可能会急剧下降。
DiscardPolicy 策略
丢弃最老的一个请求(即将被执行的下一个任务),并尝试再次提交当前任务。
DiscardOldestPolicy 策略
直接丢弃无法处理的任务,不做任何处理。
线程池的数量
Ncpu = CPU数量
Ucup = CPU的使用率
W/C = 等待时间与计算时间的比率
最优池:
Nthreads=Ncpu*Ncpu*(1+W/C)