线程过多就容易引发内存溢出,因此我们有必要使用线程池的技术
线程池的好处
降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗 提高响应速度: 当任务到达时,任务可以不需要等待线程创建就能立即执行 提高线程管理性: 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
线程池的创建
线程池的真正实现类是ThreadPoolExecutor,其中具有七个重要参数
代码语言:javascript复制public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
核心线程数。默认情况下核心线程会一直存活,但是当将allowsCoreThreadTimeOut设置为true时,核心线程也会超时回收。核心线程最开始并不会立即创建而是等任务来了再创建,然后存活下去
maximumPoolSize
最大线程数 。线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。最大线程数 = 核心线程数 非核心线程数
keepAliveTime
非核心线程闲置超时时间 。如果超过该时长,非核心线程将会回收
unit
超时单位 。指定KeepAliveTime超时时间的单位。
workQueue
任务队列 。当核心线程全被使用的时候,且任务队列未满时候就会先将任务放到任务队列中
threadFactory
线程工厂 。用于指定为线程创建新线程的方式
handler
拒绝策略 。当最大线程数达到饱和时候需要执行的饱和策略
任务队列
任务队列是基于阻塞队列实现的,即采用生产者消费者模式,在Java中需要实现BlockingQueue接口,但Java已经为我们提供了7种阻塞队列的实现
ArrayBlockingQueue 一个由数组结构构成的有界阻塞队列(数组结构可配合指针实现一个环形队列) LinkedBlockingQueue 一个由链表结构构成的有界阻塞队列,在未指明容量时默认为Integer.MAX_VALUE PriorityBlockingQueue 一个支持优先级排序的无界队列,对元素没有要求,可以实现Comparable接口来进行比较 DelayQueue 类似于PriorityBlockingQueue,是二叉堆形成的无界优先队列。要求元素都实现DelayQueue接口,通过执行时延从队列中提取任务,时间没到任务取不出来 SynchronousQueue 一个不存储元素的阻塞队列,消费者线程调用take()方法时会发生阻塞,直到有一个生产者生产了一个元素,同理生产者调用put()方法 LinkedBlockingDeque 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样FIFO也可以像栈一样FILO LinkedTransferQueue 这是一个无界队列
拒绝策略
当线程池的线程达到最大线程数时,需要执行拒绝策略,拒绝策略需要实现RejectedExecutionHandler接口,并实现rejectedExcution方法,Executors为我们实现了4种拒绝策略
- AbortPolicy(默认):丢弃任务并抛出异常
- CallerRunsPolicy:由调用线程处理该任务
- DiscardPolicy:丢弃任务,但是不抛出异常
- DiscardOldestPolicy:丢弃队列中最早未处理的异常
功能线程池
定长线程池(newFixedThreadPool)
代码语言:javascript复制 public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
特点 : 只有核心线程,线程数量固定,全是核心线程任务队列为链表的有界队列 应用场景: 控制线程最大并发数 缺点: 他的任务队列是LinkedBlockingQueue且没有指定容量,则当线程数满了之后就会一直放任务进任务队列,容易造成OOM
单线程化线程池(newSingleThreadExecutor)
代码语言:javascript复制 public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
与上面类似
可缓存线程池(newCachedThreadPool)
代码语言:javascript复制 public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:没有核心线程数只有非核心线程数,整个线程池表现为线程池数会根据任务量不断增长,没有增长,当任务执行完毕,空闲1分钟后释放线程 用途 :适合任务数比较密集,但每个任务执行时间较短的情况 缺点 : 如果任务多那么线程就会创建过多的线程,创建线程会很耗时并且容易导致OOM
定时线程池(newScheduledThreadPool)
代码语言:javascript复制 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
特点: 和性能线程固定,非核心线程很多,执行完立即删除 应用场景: 执行定时或周期性的任务 缺点: 线程数量上限是Integer.MAX_VALUE
线程池中的方法
void execute(Runnable command); 执行任务
Future submit(Callable task) 提交任务task,用返回值Future获取任务执行结果
List<Future> invokeAll(Collection<? extends Callable> tasks) 提交tasks中所有的任务
List<Future> invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit) 提交tasks中所有的任务带超时时间
T invokeAny(Collection<? extends Callable> tasks) 提交tasks中的任务那个先执行完毕返回该任务的结果其它任务取消
T invokeAny(Collection<? extends Callable> tasks,long timeout, TimeUnit unit) 提交tasks中的任务那个先执行完毕返回该任务的结果其它任务取消带超时时间
shutdown与shutdownnow
shutdown 线程池状态修改为SHUTDOWN 不会接收新的任务,但已提交的会执行完, 只打断空闲线程 shutdownnow 修改线程池状态为stop 不接收新任务、打断所有线程、会将队列中的任务返回