前言
线程池创建方式
1、通过手动创建new FixedThreadPool()、new SingleThreadExecutor()、new CachedThreadPool()等
2、通过Executors.newFixedThreadPool()等
3、通过ThreadPoolExecutor构造方法创建线程池等
手动new和通过Executors创建返回对象都为ExecutorService
注:在《阿里巴巴java开发手册》中,不推荐new和Executors方式创建线程池,原因是因为有局限性,不灵活。推荐使用ThreadPoolExecutor创建线程池,更加符合规范和统一管理。
本章主要讲ThreadPoolExecutor,手动new和通过Executors如何创建以及其他详细在后面会单独出一章做详细介绍。
1、Java常用线程池
n为入参 待指定数值
线程池名称 | 核心线程数 | 最大线程数 | 解释 |
---|---|---|---|
FixedThreadPool | n | n | 固定 线程池,核心线程数等于最大线程数 |
CachedThreadPool | 0 | Integer.MAX_VALUE | 缓存 线程池 |
SingleThreadPool | 1 | 1 | 单线程 线程池 |
ScheduledThreadPool | n | Integer.MAX_VALUE | 定时、周期性任务 线程池 |
2、Executors创建线程池常用方式
线程池名称 | 解释 |
---|---|
Executors.newFixedThreadPool() | |
Executors.newWorkStealingPool() | |
Executors.newCachedThreadPool() | |
Executors.newScheduledThreadPool() |
3、ThreadPoolExecutor构造方法创建线程池
本章下面重点介绍
四种构造方法,这里选取参数最全的构造方法,相关参数会在下面介绍
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
4、Executor框架
在介绍 ThreadPoolExecutor 之前,我们先介绍一下Executor框架。上一章中,我们介绍到通过 Runnable 接口和继承 Thread 类实现多线程编程时,会存在大量时间花费在线程的创建和销毁上,Executor 框架的引入就是来控制线程的创建、启动和销毁,简化多线程并发操作问题。
4.1、Executor继承关系图
4.2、Executor接口
从继承图中我们可以知道,Executor 接口是作为了 Executor 框架的顶级接口,其代码如下:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
在 Executor 中只有一个方法,入参为 Runnable 对象。很明显的可以知道,该入参肯定是我们实现 Runnable 接口的对象,入参对象重写的 run() 方法则是我们实际需要处理的业务代码。
void execute(Runnable command);
4.3、ExecutorService接口
该接口继承了Executor接口,并增加了其他方法(主要是管理线程生命周期的方法)其源码如下:
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
作用 | 方法名称 | 解释 |
---|---|---|
线程池关闭 | shutdown() | 不再接受新任务,直到所有子线程执行完才会真正关闭 |
shutdownNow() | 不再接受新任务,立马关闭 | |
线程池是否关闭 | isShutdown() | shutdown()被调用之后则为true |
线程池是否终止 | isTerminated() | 任务执行完时才返回true |
线程池是否关闭 | boolean awaitTermination(long timeout, TimeUnit unit) | timeout超时时间,unit时间单位。每timeout/unit 判断一次是否关闭 |
任务提交的三种方式 | <T> Future<T> submit(Callable<T> task) | 任务对象 Callable接口实现类,返回Future对象 |
<T> Future<T> submit(Runnable task, T result) | 任务对象 Runnable接口实现类,future.get()返回 result | |
Future<?> submit(Runnable task) | 任务对象 Runnable接口实现类,future.get()返回 null | |
批量提交不限时任务。等待所有的任务执行完成后统一返回 | <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) | |
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) | 可设置超时时间 | |
取得第一个完成任务的结果值,当第一个任务执行完成后,会调用interrupt方法将其他的任务中断。 | <T> T invokeAny(Collection<? extends Callable<T>> tasks) | |
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) | 可设置超时时间 |
4.4、AbstractExecutorService类
我们主要讲的是通过 ThreadPoolExecutor (当然也可以通过Executors,下一篇文章会讲)来创建线程池,因此 继承图中的 ScheduledExecutorService 我们这里不再讲解。大家有兴趣的可以研究下该类源码。AbstractExecutorService 源码如下:
public abstract class AbstractExecutorService implements ExecutorService {
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
// For efficiency, especially in executors with limited
// parallelism, check to see if previously submitted tasks are
// done before submitting more of them. This interleaving
// plus the exception mechanics account for messiness of main
// loop.
try {
// Record exceptions so that if we fail to obtain any
// result, we can throw the last exception we got.
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
// Start one task for sure; the rest incrementally
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try {
return doInvokeAny(tasks, false, 0);
} catch (TimeoutException cannotHappen) {
assert false;
return null;
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
long nanos = unit.toNanos(timeout);
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
final long deadline = System.nanoTime() + nanos;
final int size = futures.size();
// Interleave time checks and calls to execute in case
// executor doesn't have any/much parallelism.
for (int i = 0; i < size; i++) {
execute((Runnable)futures.get(i));
nanos = deadline - System.nanoTime();
if (nanos <= 0L)
return futures;
}
for (int i = 0; i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
if (nanos <= 0L)
return futures;
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
nanos = deadline - System.nanoTime();
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
}
5、ThreadPoolExecutor类
源码太多这里不再全部粘贴,有兴趣的可以自己研究~~我们主要讲一下使用该类创建线程池时的一些参数和几个构造方法。
核心参数如下:
参数名称 | 解释 |
---|---|
corePoolSize | 核心线程数。 |
maxmunPoolSize | 最大线程数。当核心线程全部被利用,工作队列任务已满,corePoolSize < maxmunPoolSIze 时才会创建新的线程。 |
keepAliveTime | 非核心线程活跃时间。超过该时间,线程被销毁。 |
unit | 非核心线程活跃时间单位。 |
workQueue | 工作队列,也叫阻塞队列。ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。 |
threadFactory | 线程创建时使用的工厂。 |
handler | 拒绝策略。线程池对拒绝任务的处理策略。AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。 |
5.1、workQueue工作队列(也叫阻塞队列)类型
任务流程:
任务在刚开始提交时,会直接交给核心线程执行。核心线程都在执行任务之后,任务会被保留在工作队列中。当任务越来越多,工作队列满了之后(且corePoolSize < maxmunPoolSize时),会创建新的线程来执行工作队列中的任务。
工作队列 | 名称 | 介绍 |
---|---|---|
有界工作队列 | ArrayBlockingQueue | 有界缓存队列。必须指定大小。 |
无界工作队列 | LinkedBlockingQueue | 无界缓存队列。可指定队列大小,不指定则为无界队列。 |
同步队列 | SynchronousQueue | 直接将任务直接提交给线程,不保持 |
注:LinkedBlockingQueue在使用时,如果不指定size(即无界),会导致工作队列无法堆满,而线程数无法达到maxmunPoolSize
5.2、handler拒绝策略类型
这里不再详细介绍,后面在写相关详文。
拒绝策略 | 介绍 |
---|---|
AbortPolicy | 丢弃任务并抛出RejectedExecutionException异常 |
DiscardPolicy | 丢弃掉提交的任务,但不抛出异常 |
DiscardOldestPolicy | 如果任务添加到线程池中被拒绝,线程池丢弃等待队列中最旧的任务,然后将被拒绝的任务添加到等待队列中。 |
CallerRunsPolicy | 如果任务被拒绝,由提交该任务的线程处理当前任务 |
5.3、ThreadPoolExecutor构造方法创建线程池
ThreadPoolExecutor 类一共提供了四种构造方式,来创建线程池,但都大同小异。
初始化数据
private static List<User> userList = new ArrayList<>();
//初始化任务数据 userList
private static void init(){
User zhangSan = new User(1,"zhangSan",21);
User liSi = new User(2,"liSi",22);
User wangWu = new User(3,"wangWu",23);
User zhaoLiu = new User(4,"zhaoLiu",24);
userList.add(zhangSan);
userList.add(liSi);
userList.add(wangWu);
userList.add(zhaoLiu);
}
1、方式一
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
//调用参数最全的重载方法
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
其中 defaultHandler 为默认的拒绝策略AbortPolicy
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
代码用例
@Test
void testCreateMethod1(){
CreateThreadPoolTest.init();
//创建线程池
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1,2,120L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2));
//任务list
List<Future> futures = new ArrayList<>();
//遍历任务list
for (User user : userList) {
//将每一个user提交给线程池
//submit的入参对象 可以为实现的Runnable对象,也可以为Callable对象,上面我们提到过,这里采用Callable
Future<Object> submit = executor.submit(new Callable<Object>() {
@Override
public String call() throws Exception {
//调用实际执行的业务,建议将每个任务需要处理的业务封装成方法调用并try,try的目的是为了不影响其他任务执行
try {
//打印用户名称
printCurrentThreadName(user);
} catch (Exception e) {
//打印错误日志
//....
}
//返回任务执行的结果
return "执行成功";
}
});
//添加到任务队列
futures.add(submit);
}
//执行任务
for (Future future : futures) {
try{
//get方法要求try或者直接抛出异常
future.get();
}catch (Exception e){
//打印错误日志
//....
}
}
}
/**
* 打印当前用户名称,以及处理当前用户的线程
*/
private void printCurrentThreadName(User user){
System.out.println("当前线程 ----> " + Thread.currentThread().getName()+", 执行用户 ---->" + user.getUserName());
}
执行结果
当前线程 ----> pool-1-thread-1, 执行用户 ---->zhangSan
当前线程 ----> pool-1-thread-2, 执行用户 ---->zhaoLiu
当前线程 ----> pool-1-thread-1, 执行用户 ---->liSi
当前线程 ----> pool-1-thread-1, 执行用户 ---->wangWu
Process finished with exit code 0
2、方式二
自定义线程池工厂
ThreadPoolExecutor executor
= new ThreadPoolExecutor(1,2,120L,TimeUnit.SECONDS,new LinkedBlockingQueue(2), Executors.defaultThreadFactory());
@Test
void testCreateMethod1(){
CreateThreadPoolTest.init();
//创建线程池
ThreadPoolExecutor executor
= new ThreadPoolExecutor(1,2,120L,TimeUnit.SECONDS,new LinkedBlockingQueue(2), Executors.defaultThreadFactory());
//任务list
List<Future> futures = new ArrayList<>();
//遍历任务list
for (User user : userList) {
//将每一个user提交给线程池
//通过lambda表达式
Future submit = executor.submit(() -> {
try {
printCurrentThreadName(user);
} catch (Exception e) {
}
});
//添加到任务队列
futures.add(submit);
}
//执行任务
for (Future future : futures) {
try{
//get方法要求try或者直接抛出异常
future.get();
}catch (Exception e){
}
}
//关闭线程池
executor.shutdown();
try{
executor .awaitTermination(10L, TimeUnit.MINUTES);
}catch (Exception e){
logger.error(e.getMessage(),e);
}
}
3、方式三
自定义拒绝策略
ThreadPoolExecutor executor
= new ThreadPoolExecutor(1,2,120L,TimeUnit.SECONDS,new LinkedBlockingQueue(2), new ThreadPoolExecutor.AbortPolicy());
@Test
void testCreateMethod1(){
CreateThreadPoolTest.init();
//创建线程池
ThreadPoolExecutor executor =
new ThreadPoolExecutor(1,2,120L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2), new ThreadPoolExecutor.AbortPolicy());
//任务list
List<Future> futures = new ArrayList<>();
//遍历任务list
for (User user : userList) {
//将每一个user提交给线程池
//通过Runnable匿名内部类
Future submit = executor.submit(new Runnable() {
@Override
public void run() {
try {
printCurrentThreadName(user);
} catch (Exception e) {
}
}
});
//添加到任务队列
futures.add(submit);
}
//执行任务
for (Future future : futures) {
try{
//get方法要求try或者直接抛出异常
future.get();
}catch (Exception e){
}
}
//关闭线程池
threadPoolExecutor.shutdown();
try{
executor .awaitTermination(10L, TimeUnit.MINUTES);
}catch (Exception e){
logger.error(e.getMessage(),e);
}
}
4、方式四
参数最全的自定义重载方法
ThreadPoolExecutor executor
= new ThreadPoolExecutor(1,2,120L,TimeUnit.SECONDS,new LinkedBlockingQueue(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
以上已介绍过通过Runnable和Callable方式提交,这里不再赘述