一.初始化线程的4种方式
1.继承Thread
//继承Thread
public static void main(String[] args) {
System.out.println("----------主线程开始---------");
//继承Thread
Thread01 thread01 = new Thread01();
thread01.start();
System.out.println("-------主线程结束------");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("当前线程运行结果"+i);
}
}
2.实现Runable:
这种方式更为灵活,因为java是单继承,如果你的类已经继承了其他类,你仍然可以实现Runable来实现多线程。
public static void main(String[] args) {
System.out.println("----------主线程开始---------");
Runable01 runable01 = new Runable01();
Thread thread = new Thread(runable01);
thread.start();
System.out.println("-------主线程结束------");
}
public static class Runable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("当前运行结果:" + i);
}
}
3.实现Callable接口+FutureTask:
Callable接口允许线程返回结果,并且可以抛出异常,结合FutureTask可以异步获取线程执行结果。
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("----------主线程开始---------");
Callable01 callable01 = new Callable01();
FutureTask<Integer> task = new FutureTask<>(callable01);
Thread thread = new Thread(task);
thread.start();
//阻塞等待整个线程执行完,获取返回结果
Integer integer = task.get();
System.out.println("------获取的返回结果-------"+integer);
System.out.println("-------主线程结束------");
}
public static class Callable01 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("当前运行结果:" + i);
return i;
}
}
4.线程池:
使用线程池可以管理和复用线程,避免频繁创建和销毁线程,提高了资源利用率。
//初始化线程池
//Executors.newFixedThreadPool(3);
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
100,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("----------主线程开始---------");
executor.execute(new Runnable01());
System.out.println("-------主线程结束------");
}
public static class Runnable01 implements Runnable {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("当前运行结果:" + i);
}
}
通过线程池性能稳定,也可以获取执行结果,并捕获异常,但是在业务复杂的情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。
二.线程池
1.线程池的七大参数
(1)corePoolSize[5]:核心线程池【一直存在,除非allowCoreThreadTimeOut】,创建好以后就准备就绪的线程数量,等待接收异步任务去执行5个 Thread thread = new Thread(),thread.start。
(2)maximumPoolSize[200]:最大线程数,控制资源。
(3)keepAliveTime:存活时间。
(4)unit:时间单位。
(5)BlockingQueue<Runnable> workQueue:阻塞队列,如果任务有很多,就会将多的任务放在队列里,只要线程空闲,就会去队列里取出新的任务继续执行。
(6)ThreadFactory:创建线程的工厂。
(7)RejectedExecutionHandler handler:如果队列满了,按照我们指定的策略,拒绝执行。
2.运行流程
线程池创建,准备好core数量的核心线程,准备接受任务。
新的任务进来,用core准备好的空闲线程执行。
(1)core满了,就将再次进来的任务放到阻塞队列中,空闲的core会自己去阻塞队列获取任务执行。
(2)阻塞队列满了,就直接开启新线程执行,最大只能开到max指定的数量。
(3)max都执行好了,max-core数量空闲的线程会在keepAliveTime 指定的时间后自动销毁,最终保持到core大小。
(4)如果线程开到max的数量,还有新任务进来,就会使用reject指定的拒绝策略来进行处理。
所有线程的创建都是由指定的Factory创建的。
面试题:一个线程池core=8,max=20,queue=50,100并发进来怎么分配的
首先会有8个线程进行执行,接下来50个线程会到线程队列中排队,因为最大线程为20,所以它会在开20-8=12个线程去执行,现在有70个线程已经安排上了,那么剩下的30个线程会执行默认的拒绝策略。
三.CompletableFuture组合式异步编程
(小伙伴不要去死记这些方法,具体应用我们要根据自己的业务来查找对应的编排方法,写法大差不差一样,所以我们知道怎么用即可,没必要浪费时间去记各个用法)
CompletableFuture是Java 8中引入的一个功能强大的异步编程工具类,它基于Future和CompletionStage接口构建,主要用于异步执行任务并返回结果,实现异步计算和操作组合。
异步执行:他可以在新的线程上执行计算或者操作,而不会阻塞主线程,提高线程的响应速度
可组合性:其操作可以组合成多个CompletableFuture构成复杂的异步计算链,使我们更容易的构成复杂的异步操作流水线
异常处理:通过exceptionally()方法来捕获异常并返回默认值
取消与超时:可以取消异步任务,还可以设置超时时间来避免任务的无限期等待
非阻塞式等待:提供了非阻塞式等待方法,如join()和getNow()方法
1.创建异步对象
(1)runAsync和supplyAsync
CompletableFuture提供了四种静态方法来创建一个异步操作
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)
没有指定Executor的方法会使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,如果指定线程池,则使用指定的线程池运行,以下所有的方法都类同
-
runXxxx都是没有返回结果的,supplyXxxx都是可以获取返回结果的
-
可以传入自动义的线程池,否则就用默认的线程池
代码如下:
//初始化一个线程池
public static ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
//runAsnyc
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> {
System.out.println("------runAsync当前线程--------");
int i = 10 / 2;
System.out.println("runAsync当前运行结果:" + i);
}, executor);
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("supplyAsync当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("supplyAsync当前运行结果:" + i);
return i;
}, executor);
Integer integer = supplyAsync.get();
System.out.println("supplyAsync获取当前运行结果:" + integer);
System.out.println("----------主线程结束----------");
}
2.计算结果完成时的回调方法
当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action,主要是下边的方法:
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
whenComplete可以处理正常和异常的计算结果,exceptionally 处理异常情况。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete:它的回调函数会在执行当前任务的线程上进行执行,也就是说,如果当前CompletableFuture是在某个特定的线程上完成的,,那么whenComplete的回调函数也会在该线程上执行。
whenCompleteAsync 他的回调函数会被提交给线程池(如果提供了),或者默认 ForkJoinPool.commonPool 线程池来执行,也就是说,即使compleableFuture是在某个特定的线程上完成,whenCompleteAsync的回调函数也是在另一个线程上执行。
代码如下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("supplyAsync当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("supplyAsync当前运行结果:" + i);
return i;
}, executor).whenComplete((result,exception)->{
System.out.println("异步任务完成了----结果是"+result+"异常是"+exception);
}).exceptionally(throwable -> 10);
Integer integer = supplyAsync.get();
System.out.println("supplyAsync获取当前运行结果:" + integer);
System.out.println("----------主线程结束----------");
}
handle 方法
handle是完成任务时对结果的处理,handle方法和thenApply方法处理的方式基本一样,不同的是handle是在任务完成之后去执行的,还可以处理异常任务,而thenApply只能处理执行正常的任务,任务执行异常则不执行thenApply。
public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn);
public <U> CompletionStage<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn,Execut
和complete一样,可对结果进行处理,可改变返回值
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Integer> handleFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("handleFuture当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("handleFuture当前运行结果:" + i);
return i;
}, executor).handle((result,excetion)->{
if (result!=null){
return result*2;
}
if (excetion!=null){
return 0;
}
return 0;
});
Integer integer = handleFuture.get();
System.out.println("最终的结果是:"+integer);
}
线程串行化
public <U > completableFuture < U > thenApply(Function < ? super T, ? extends U > fn);
public <U > completableFuture < U > thenApplyAsync(Function < ? super T, ? extends U > fn);
public <U > CompletableFuture < U > thenApplyAsync(Function < ? super T, ? extends U > fn, Executor executor);
public completionStage<Void> thenAccept (consumer < ? super T > action);
public Completionstage<Void> thenAcceptAsync (Consumer < ? super T > action);
public Completionstage<Void> thenAcceptAsync (consumer < ? super T > action, Executor executor);
public CompletionStage<Void> thenRun (Runnable action);
public Completionstage<Void> thenRunAsync (Runnable action);
public Completionstage<Void> thenRunAsync (Runnable action, Executor executor);
thenApply方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值
thenAccept方法:消费处理结果,接收到结果后进行处理,并消费掉,没有返回结果
thenRun方法:只要上面的任务执行完,就开始执行thenRun,只有处理完任务后,执行thenRun的后续操作,不能获取上一步的执行结果
带有Async默认是异步执行的,同之前。以上都要前置任务成功完成。Function<? super T,? extends U>
T:上一个任务返回的结果类型 U:当前任务的返回值类型
代码如下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
CompletableFuture<String> thenApplyAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("thenApplyAsync 当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("thenApplyAsync 当前运行结果:" + i);
return i;
}, executor).thenApplyAsync(res->{
System.out.println("thenApplyAsync任务2启动了。。。。"+res);
return "hello"+res;
},executor);
//拿到结果
String s = thenApplyAsync.get();
System.out.println("thenApplyAsync返回结果:"+s);
//thenAcceptAsync能接收上一步的返回结果,但是没有返回值
CompletableFuture<Void> thenAcceptAsync = CompletableFuture.supplyAsync(()->{
System.out.println("thenAcceptAsync当前线程:" + Thread.currentThread().getId());
int i = 10 / 5;
System.out.println("thenAcceptAsync当前运行结果:" + i);
return i;
},executor).thenAcceptAsync(res->{
System.out.println("thenAcceptAsync任务2启动了。。。。" + res);
},executor);
// 4.3、thenRun 不能获取得到上一步的执行结果
CompletableFuture<Void> thenRunAsync = CompletableFuture.supplyAsync(() -> {
System.out.println("thenRunAsync当前线程:" + Thread.currentThread().getId());
int i = 10 / 5;
System.out.println("thenRunAsync当前运行结果:" + i);
return i;
}, executor).thenRunAsync(() -> {
System.out.println("thenRunAsync任务2启动了。。。。");
}, executor);
}
两任务组合-都要完成
两个任务都必须完成,触发该任务
-
runAfterBoth
两个future不需要获取future的结果,只需要两个future处理完任务后,处理该任务
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action);
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor
-
thenAccetpBoth
组合两个 future,获取两个future任务的返回结果,然后处理任务,没有返回结果
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor);
-
thenCombine
组合两个future,获取两个future的返回结果,并返回当前任务的返回值
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
代码如下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(()->{
System.out.println("------任务线程1-----"+Thread.currentThread().getId());
int i = 10/5;
System.out.println("----任务线程1结束");
return i;
},executor);
CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(()->{
System.out.println("------任务线程2-----"+Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务线程2结束");
return "hello";
},executor);
//不能得到两个任务的结果,也没有返回值
future01.runAfterBothAsync(future02,()->{
System.out.println("----runAfterBothAsync任务三开始---");
},executor);
//能得到两个任务的结果,但是没有返回值
future01.thenAcceptBothAsync(future02,(r1,r2)->{
System.out.println("----thenAcceptBothAsync任务三开始。。。之前的结果"+r1+":"+r2);
},executor);
//得到两个任务的参数并返回结果
CompletableFuture<String> thenCombineAsync = future01.thenCombineAsync(future02,(r1,r2)->{
System.out.println("----thenCombineAsync任务三开始。。。之前的结果" + r1 + ":" + r2);
return r1 + ":" + r2 + "->无敌是多么寂寞";
},executor);
System.out.println("thenCombineAsync返回结果:" + thenCombineAsync.get());
System.out.println("---------主线程结束--------");
}
两任务组合-只要有一个任务完成就执行第三个
当两个任务中,任意一个任务执行完的时候,执行任务
-
runAfterEither方法
两个任务有一个执行完,不需要处理结果,处理任务也没有返回值
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action,Executor executor);
-
acceptEither
两个任务有一个执行完,获取它的返回值,处理任务,没有新的返回值
public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super
-
applyToEither方法
两个任务有一个处理完成,获取他的返回值,处理任务并返回新的返回值
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? sup
代码如下:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
CompletableFuture<Object> future001 = CompletableFuture.supplyAsync(() -> {
System.out.println("future001任务1线程:" + Thread.currentThread().getId());
int i = 10 / 5;
System.out.println("future001任务1线程结束");
return i;
}, executor);
CompletableFuture<Object> future002 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:" + Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future002任务2线程结束");
return "hello";
}, executor);
//runAfterEitherAsync 不感知结果,自己也无返回值
future001.runAfterEitherAsync(future002, () -> {
System.out.println("runAfterEitherAsync任务三开始。。。");
}, executor);
//accetpEitherAsync 感知结果,自己没有返回值
future001.acceptEitherAsync(future002, (res) -> {
System.out.println("acceptEitherAsync任务三开始。。。" + res);
}, executor);
//applyToEitherAsync
CompletableFuture<String> stringCompletableFuture = future001.applyToEitherAsync(future002, (res) -> {
System.out.println("applyToEitherAsync任务三开始。。。" + res);
return res.toString() + "-> haha";
}, executor);
System.out.println("applyToEitherAsync返回结果:" + stringCompletableFuture.get());
System.out.println("---------主线程结束--------");
}
-
thenCompose
thenCompose方法,允许你对两个CompletionStage 进行流水线操作,第一个操作完成时,将其结果作为参数传递给第二个操作。
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) ;
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage
多任务组合
public static completableFuture<Void> all0f(completableFuture<?>... cfs);
public static completableFuture<Obiect> anyof(completableFuture<?>... cfs);
-
allOf:等待所有任务完成
-
anyOf:只要有一个任务完成
allOf代码:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的属性");
return "黑色+256g";
}, executor);
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("查询商品的图片信息");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello.jpg";
}, executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的介绍");
return "华为";
}, executor);
CompletableFuture<Void> allOf = CompletableFuture.allOf(futureAttr, futureDesc, futureImg);
allOf.get();//等待所有线程执行完
System.out.println("allOf获取结果:" + futureAttr.get() + "=>" + futureImg.get() + "=>" + futureDesc.get());
System.out.println("---------主线程结束--------");
}
anyOf代码:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("---------主线程开始--------");
CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的属性");
return "黑色+256g";
}, executor);
CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
System.out.println("查询商品的图片信息");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello.jpg";
}, executor);
CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的介绍");
return "华为";
}, executor);
CompletableFuture<Object> future = CompletableFuture.anyOf(futureAttr, futureDesc, futureImg);
future.get();
System.out.println("anyOf获取结果:" + futureAttr.get() + "=>" + futureImg.get() + "=>" + futureDesc.get());
System.out.println("---------主线程结束--------");
}
附:线程池代码配置
yml:
#线程池属性的配置
test:
thread:
core: 20
max-size: 200
keep-alive-time: 10
新建配置类
@Data
@Component
@ConfigurationProperties(prefix = "test.thread")
public class ThreadPoolConfigProperties {
private Integer core;
private Integer maxSize;
private Integer keepAliveTime;
}
线程池配置,获取线程池的属性值这里直接调用与配置文件相对应的属性配置类
//如果ThreadPoolConfigProperities.class类上没有加上@Component注解
// 那么我们在需要配置类里开启属性配置的类加载到容器
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
return new ThreadPoolExecutor(pool.getCore(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
为了使我们的任务进行的更快,我们可以让查询的各个子任务线程执行,但是由于各个任务之间可能有依赖的一些关系,因此就涉及到了异步编排.
根据我们的业务需求,选择合适的编排,不需要死记硬背,主要还是在业务上。