ThreadPoolExecutor线程池使用
简介
线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。—来自百度百科
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize:核心池的大小,就是正式线程的数量和;
- maximumPoolSize:最大池大小,正式线程数量和临时线程的总数;
- keepAliveTime:空闲的多余线程保持时间,临时线程不执行任务时能够存活的时间;
- TimeUnit:时间单位
- BlockingQueue:阻塞式队列,正式线程都忙时将线程放到workQueue中,workQueue大小是n。
- ThreadFactory:当线程池需要新的线程时,会使用ThreadFactory来生成新的线程。
- RejectedExecutionHandler:由于线程无法接收你所提交的任务的拒绝策略。
线程添加策略
- 在线程池刚创建出来时,线程池中没有任何线程,当有任务提交过来时,如果线程池中管理的线程的数量小于corePoolSize,则无论是否有闲置的线程都会创建新的线程来使用.而当线程池中管理的线程的数量达到了corePoolSize,再有新任务过来时,会复用闲置的线程。
- 当所有的核心池大小中的线程都在忙碌,则再有任务提交,会存入workQueue中,进行排队,当核心池大小中的线程闲置后,会自动从workQueue获取任务执行。
- 而当所有的核心池大小中的线程都在忙碌,workQueue也满了,则会再去创建新的临时线程来处理提交的任务,但是,无论如何,总的线程数量,不允许超过maximumPoolSize。
- 而当所有的核心池大小中的线程都在忙碌,workQueue也满了,也创建了达到了maximumPoolSize的临时线程,再有任务提交,此时会交予RJHandler来拒绝该任务。
- 当任务高峰过去,workQueue中的任务也都执行完成,线程也依次闲置了下来,则在此时,会将闲置时间超过keepAliveTime(单位为unit)时长的线程关闭掉,但是关闭时会至少保证线程池中管理的线程的数量 不少于corePoolSize个。
提交任务
execute
只能提交Runnable类型的任务,无返回值。在执行任务时,如果遇到异常会直接抛出。
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
threadPoolExecutor.execute(()->{
log.info("executorService test");
});
submit
既可以提交Runnable类型的任务,也可以提交Callable类型的任务,会有一个类型为Future的返回值,但当任务类型为Runnable时,返回值为null。
submit在执行任务时,如果遇到异常不会直接抛出,只有在使用Future的get方法获取返回值时,才会抛出异常。
Future<?> submit_method = executorService.submit(new Runnable() {
@Override
public void run() {
log.info("submit Runnable 。。。");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
try {
Object o = submit_method.get();//检查线程是否执行完成,未完成时阻塞
log.info("submi Runnable is over, return ={}", o);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Future<String> submit = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("submit Callable 。。。");
Thread.sleep(3000L);
return "ok";
}
});
try {
String s = submit.get();
log.info("submit Callable is over, return={}", s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
执行结果
invokeAny
场景:启动多个线程,相互独立的(无同步)去计算一个结果,当某一个线程得到结果之后,立刻终止所有线程,因为只需要一个结果就够了。
invokeAny()定义在AbstractExecutorService抽象类中,接收的参数是一个List,List中的每一个元素必须实现Callable接口。他的功能是依此启动新的线程执行List中的任务,并将第一个得到的结果作为返回值,然后立刻终结所有的线程。其用法如下:
List<Callable<String>> callables = new ArrayList<>();
callables.add(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(3000L);
return "任务1";
}
});
callables.add(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000L);
return "任务2";
}
});
try {
String s = threadPoolExecutor.invokeAny(callables);
log.info(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
执行结果
invokeAll
阻塞式,必须等待所有任务执行完成后统一返回,可以设置超时时间,这里的超时时间是针对的所有tasks,而不是单个task的超时时间。如果超时,会取消没有执行完的所有任务,并抛出超时异常。相当于将每一个future的执行情况用一个list集合保存,当调用future.get()方法取值时和设置的timeout比较,是否超时。
这里提交的任务容器列表和返回的Future列表存在顺序对应的关系。
源码
try {
List<Future<String>> futures = threadPoolExecutor.invokeAll(callables);
futures.stream().forEach(f -> {
try {
log.info("获取结果、、、");
log.info(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
try {
List<Future<String>> futures = threadPoolExecutor.invokeAll(callables, 1000, TimeUnit.MILLISECONDS);
futures.stream().forEach(f -> {
try {
log.info("获取结果、、、");
log.info(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
执行结果
shutdown
将线程池状态置为SHUTDOWN。平滑的关闭ExecutorService,当此方法被调用时,ExecutorService停止接收新的任务并且等待已经提交的任务(包含提交正在执行和提交未执行)执行完成。当所有提交任务执行完毕,线程池即被关闭。
shutdownNow
将线程池状态置为STOP。跟shutdown()一样,先停止接收外部提交的任务,忽略队列里等待的任务,尝试将正在跑的任务interrupt中断,返回未执行的任务列表。
isShutdown
当调用shutdown()或shutdownNow()方法后返回为true。
isTerminated
当调用shutdown()方法后,并且所有提交的任务完成后返回为true;
当调用shutdownNow()方法后,成功停止后返回为true;
awaitTermination
接收timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。
停止线程池
/**
* 停止线程池
* 先使用shutdown,停止接收新的任务并尝试完成所有已存在的任务。
* 如果超时,则调用shutdownNow,取消在workQueue中Pending的任务,并中断所有阻塞函数。
* 如果仍然超时,则强制退出。
* 另外在shutdown时线程本身被调用中断做了处理
* @param pool
*/
public static void shutdownAndAwaitTermination(ExecutorService pool) {
if (pool != null && !pool.isShutdown()) {
pool.shutdown();
try {
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
log.info("pool did not termination");
}
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}