十四,线程池详解
之前我们已经了解ExecutorService接口声明的方法:
public abstract <T> T invokeAny(Collection<? extends Callable<T>>) throws InterruptedException,ExecutionException
public abstract <T> T invokeAny(Collection<? extends Callable<T>>,long,TimeUnit) throws InterruptedException,ExecutionException,TimeoutException
public abstract <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>>) throws InterruptedException
public abstract <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>>,long,TimeUnit) throws InterruptedException
public abstract <T> Future<T> submit(Runnable,T)
public abstract <T> Future<T> submit(Callable<T>)
public abstract boolean awaitTermination(long,TimeUnit) throws InterruptedException
public abstract boolean isShutdown()
public abstract boolean isTerminated()
public abstract List<Runnable> shutdownNow()
public abstract Future<?> submit(Runnable)
public abstract void execute(Runnable) 继承自Executor这个顶级接口
public abstract void shutdown()
API线程池使用示例
使用线程池要自己设置参数,做到每个参数心中有数。
下面具体举个线程池的例子,这也是API中Executors.newCachedThreadPool();的实现
public class ThreadPoolTool {
public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
public static void execute(Runnable command){
THREAD_POOL_EXECUTOR.execute(command);
}
}
线程池几个参数的说明
参数名 | 说明 |
---|---|
corePoolSize | 核心线程数,一直存活,当小于核心线程数的时候优先创建(只有在工作队列满的情况下才会创建超出这个数量的线程) |
maximumPoolSize | 线程池允许的最大线程数,当大于核心线程数且队列满,创建直到这个数 |
keepAliveTime | 空闲线程超时间,当线程数多于核心线程数时,空闲的线程会在这个时间退出,直到线程数等于核心线程数 |
BlockingQueue<Runnable> workQueue | 阻塞队列,如果是ArrayBlockingQueue可以限制队列大小 |
BlockingQueue是个接口,有几个很重要的子类
下面的表格要熟
Summary of BlockingQueue methods
Throws exception | Special value | Blocks | Times out |
---|---|---|---|
Insert | add(e) | offer(e) | put(e) |
Remove | remove() | poll() | take() |
Examine | element() | peek() | not applicable |
它有这么几个常用和不常用子类
子类名 | 说明 |
---|---|
LinkedBlockingDeque | 双向队列(双向链表实现,jedisPool用的正是这个) |
LinkedBlockingQueue | 队列(单链表) 使用频率高 |
ArrayBlockingQueue | 限制大小的队列 |
SynchronousQueue | 空集合,put必须等待一个take,Executors.newCachedThreadPool();用的正是这个 |
ScheduledThreadPoolExecutor.DelayedWorkQueue | 内部类不常用 |
线程池的几个例子
API | 返回类型 | 说明 |
---|---|---|
Executors.newFixedThreadPool(nThreads); | ExecutorService | 固定线程数的线程池,不可伸缩 |
Executors.newSingleThreadExecutor | ExecutorService | 单线程,等同于newFixedThreadPool(1) |
Executors.newCachedThreadPool() | ExecutorService | 相当于无限线程,空闲线程默认60秒超时销毁,适合于短任务 |
Executors.newScheduledThreadPool(corePoolSize); | ScheduledExecutorService | 支持定时周期执行任务 |
上面已经给出了Executors.newCacheThreadExector的例子
下面给是其他几个API的
串行Executors.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
固定大小Executors.newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我们怎么优雅的使用线程池和他的接口?
public interface ThreadPool {
Executor getExecutor();
}
public class FixedThreadPool implements ThreadPool {
public Executor getExecutor() {
String name = ThreadPoolConstants.DEFAULT_THREAD_NAME;
int threads = ThreadPoolConstants.DEFAULT_THREADS;
int queues = ThreadPoolConstants.DEFAULT_QUEUES;
return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,
queues == 0 ? new SynchronousQueue<Runnable>() :
(queues < 0 ? new LinkedBlockingQueue<Runnable>()
: new LinkedBlockingQueue<Runnable>(queues)),
new NamedThreadFactory(name, true), new AbortPolicyWithReport(name));
}
}
体会下
对于线程池的线程数大小,举个例子,假设corePoolSize是5,maxPoolSize大小是10,阻塞队列大小是4。
当有任务进来的时候,线程池从0开辟新的线程达到1。不管这个线程有没有结束新的任务来了会在开辟新的线程而不是复用第一个,直到达到核心线程数5。如果此时有更多的并发任务上来,则放大任务队列里,如果任务队列满了,则开辟新线程直到最大线程数,如果最大线程数也满了则执行拒绝策略