一、线程池3种常用方式
ExecutorService poolExecutor = Executors.newCachedThreadPool();
Executors.newFixedThreadPool(5);
Executors.newCachedThreadPool();
但是阿里巴巴的java开发手册规定,不允许手动创建线程,必须使用线程池;同时线程池不能用Executors
来获取,必须通过ThreadPoolExecutor
。
二、线程池7大参数
点进上面这几个创建线程池的方法中可以发现,其背后都是创建了一个名叫ThreadPoolExecutor
的对象,该对象的创建依赖7个参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:指定线程池的核心线程数。
- maximumPoolSize:指定线程池的最大线程数。与核心线程数的区别就是核心线程数是一直活跃的,最大线程数包括核心线程数和一部分不活跃的线程。当核心线程和队列都占用的情况下就会新开线程【不会超过最大线程】处理请求;请求处理完成后,这些超过核心线程的新启动的线程就会在超过最大空闲时间被释放。
- keepAliveTime:最大空闲时间。当核心线程不够而创建了新线程,新线程的最大空闲时间,超过这个时间新线程就会被释放。
- unit:最大空闲时间单位。
- workQueue:阻塞队列。用于存放积压的任务。
- threadFactory:创建新线程的工厂,一般使用默认就可。
- handler:拒绝策略。
三、线程池工作原理
- 第一步:会判断核心线程数是否已满,如果没有就执行;如果满了就执行第二步。
- 第二步:判断阻塞队列是否已满。如果没有就加入阻塞队列;如果满了就执行第三步。
- 第三步:判断最大线程数是否已满。如果没有就创建线程来执行;如果满了就执行拒绝策略。
四、线程池拒绝策略
JDK内置的几种拒绝策略:
- AbortPolicy(默认):直接抛异常
RejectedExecutionException
。 - CallerRunsPolicy:将任务交给调用者执行。main线程调用就交给main线程执行。
- DiscardOldestPolicy:抛弃队列中等待最久的任务。
- DIscardPolicy:丢弃任务。
AbortPolicy
:
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 直接抛出异常
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
CallerRunsPolicy
:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
交给调用者执行。
DiscardOldestPolicy
:
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
将队列头元素抛弃。
DiscardPolicy
:
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
啥也不做,就是抛弃了新的任务。
内置的几种拒绝策略都继承了RejectedExecutionHandler
接口,按照这种逻辑,就可以对拒绝策略有自己的扩展。
public class MyRejectedPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("拒绝执行...");
}
}
五、生产中的线程池
生产中一般通过ThreadPoolExecutor
来创建线程池,所以线程数的设置就相当考究。
根据业务的类型可以分为:
- CPU密集型
- IO密集型
当然,跟服务器的硬件配置更是密切相关。
CPU密集型指任务需要大量运算,没有阻塞,CPU一直跑着。CPU密集型一般分配尽可能少的线程数量。
一般公式:CPU核数+1
IO密集型由于不是一直执行任务,应该配置多的线程:比如CPU核数 * 2
但是据小道消息,业内也有使用分配线程数:
CPU核数 / (1-阻塞系数);阻塞系数在0.8~0.9之间。
按照这个公式,一个8核CPU最大可分配的线程数就是:
8 / (1 - 0.9) = 80
个线程数。
六、异步编排
1、创建异步对象
2、计算完成时回调方法
whenComplete可以处理正常和异常的计算结果,exceptionally处理异常情况。
whenComplete和whenCompleteAsync的区别:
- whenComplete:是执行当前任务的线程继续执行whenComplete任务
- whenCompleteAsync:把whenCompleteAsync这个任务继续提交给线程池执行
总结:方法以aync结尾的,都会把任务提交给线程池执行,不以async结尾的使用当前线程继续执行。
public static void useSupplyAsyncStartAThreadAndComplete() throws ExecutionException, InterruptedException {
System.out.println("线程开始...");
CompletableFuture.supplyAsync(() -> {
int i = 10 / 0;
return i;
}, executor).whenComplete((res, exception) -> {
System.out.println("执行结果是 -> " + res + " 执行异常是 -> " + exception);
}).exceptionally((e) -> {
return 20; // 这个结果会在线程执行异常时覆盖掉supplyAsync中的返回值
});
}
3、handle方法
与complete一样,可以处理线程执行结果(包括异常),也有返回值。
public <U> CompletableFuture<U> handle(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(null, fn);
}
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(asyncPool, fn);
}
public <U> CompletableFuture<U> handleAsync(
BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {
return uniHandleStage(screenExecutor(executor), fn);
}
public static void useSupplyAsyncStartAThreadAndHandle() throws ExecutionException, InterruptedException {
System.out.println("线程开始...");
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor).handle((res, e) -> {
if (res != null) {
return res;
}
if (e != null) {
System.out.println(e);
return 11;
}
return 0;
});
System.out.println("最终结果 -> " + future.get());
}
4、线程串行化方法
// 串行化测试demo
public static void threadSerial(){
/**
* 1、thenRun、thenRunAsync 不会接收上一个线程执行结果作为参数、不会有返回
* 2、.thenAcceptAsync/.thenAccept(res -> {
* }): 可以接收上一个线程执行结果作为参数,但不会有返回值
* 3、thenApply/.thenApplyAsync(res -> {
* return 1;
* }); : 可以接收上一个线程执行结果作为参数,而且会有返回值
*/
CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor).thenApplyAsync(res -> {
return 1;
});
}
5、两任务组合——都要完成
// 多任务组合测试demo【都完成】
/**
* runAfterBoth/runAfterBothAsync: future1和future2执行结束后执行新线程中的,不能接收future2和future1的执行结果作为参数。也不能有返回值
*/
public static void multiTaskThread_runAfterBoth(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
future1.runAfterBoth(future2, () -> {
try {
System.out.println(future1.get());
System.out.println(future2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("world");
});
}
// 多任务组合测试demo【都完成】
/**
* thenAcceptBoth/thenAcceptBothAsync: future1和future2执行结束后执行新线程中的,可以接收future2和future1的执行结果作为参数。但不能有返回值
*/
public static void multiTaskThread_thenAcceptBothAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
future1.thenAcceptBothAsync(future2, (res, res1) -> {
System.out.println(res + " -> " + res1 + " -> world");
});
}
// 多任务组合测试demo【都完成】
/**
* thenCombine/thenCombineAsync: future1和future2执行结束后执行新线程中的,可以接收future2和future1的执行结果作为参数。也可以有返回值
*/
public static void multiTaskThread_thenCombineAsync() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
CompletableFuture<String> result = future1.thenCombineAsync(future2, (res, res1) -> {
return res + " -> " + res1 + " -> world";
}, executor);
System.out.println(result.get());
}
6、两任务组合——一个完成
// 多任务组合测试demo【一个完成】
/**
* runAfterEitherAsync/runAfterEither:其中组合执行线程中一个线程执行结束就执行后面的线程。不能接收参数,也没有返回值
*/
public static void multiTaskThread_runAfterEitherAsync(){
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
future1.runAfterEitherAsync(future2, () -> {
System.out.println("world");
});
}
// 多任务组合测试demo【一个完成】
/**
* acceptEitherAsync/acceptEither:其中组合执行线程中一个线程执行结束就执行后面的线程【这几个线程的返回值类型必须相同】。能接收参数,但是没有返回值
*/
public static void multiTaskThread_acceptEitherAsync(){
CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
future1.acceptEitherAsync(future2, (res) -> {
System.out.println("world");
});
}
// 多任务组合测试demo【一个完成】
/**
* applyToEitherAsync/applyToEither: 其中组合执行线程中一个线程执行结束就执行后面的线程【这几个线程的返回值类型必须相同】。能接收参数,也有返回值
*/
public static void multiTaskThread_applyToEitherAsync(){
CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
future1.applyToEitherAsync(future2, (res) -> {
return "world";
});
}
7、多任务组合
// 多任务组合 【等待所有组合的线程执行结束】
public static void multiTaskThread_allOf() throws ExecutionException, InterruptedException {
CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
CompletableFuture<Void> future = CompletableFuture.allOf(future1, future2);
future.get(); // 等待future1和future2执行结束
/**
* 注意:CompletableFuture.allOf返回的future.get并不能获取到future1或者 future2的执行结果
* 要获取future1或者future2的执行结果还是需要调用各自的get方法
*/
System.out.println(future1.get());
System.out.println(future2.get());
}
// 多任务组合 【等待所有组合的线程中一个执行结束】
public static void multiTaskThread_anyOf() throws ExecutionException, InterruptedException {
CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
int i = 10 / 2;
return i;
}, executor);
CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
return "Hello";
}, executor);
CompletableFuture<Object> future = CompletableFuture.anyOf(future1, future2);
future.get(); // 等待future1和future2执行结束
/**
* 注意:CompletableFuture.anyOf返回的future.get只能获取成功线程的返回值
*/
System.out.println(future1.get());
System.out.println(future2.get());
}