多线程回顾
什么是线程?
**什么是线程?**操作系统下中每一个运行的进程都包含多个线程的执行,比如打开王者荣耀,你操作英雄的同时,你也可以接收到小学生队友发来的文字消息和听到各种击杀音效,这样一来,你玩王者的同时就可以享受到,视觉,听觉,与小学生交流等多样的游戏体验!
实际上以上的方式都是由多个线程同时协作执行的,多线程的目的就是让我们能够在一个时间点高效的干多件事!!
线程池是什么?项目中为什么一点要使用线程池?
-
顾名思义,线程池其实就是一个 容纳多个线程的容器 ,其中的线程可以反复使用,省去了频繁创建线程对象的操作 ,
-
从java虚拟机的层面上来讲,当我们创建一个线程时,都需要尝试在堆区域进行创造对象分配空间,而当系统用户量大时,频繁的创造线程是非常开销大的,甚至都有可能出现OOM的现象!于是使用线程池,应用这种缓存思想,尽量的减少了我们频繁创建线程的开销
多线程的实现方式
多线程:
1)、继承 Thread
2)、实现 Runnable 接口
3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
FutureTask<Integer> futureTask
= new FutureTask<>(new MyCallable());
// 【new FutureTask<>(new MyRunnable(), result)】
//可以接受runnable的返回值
new Thread(futureTask).start();
4)、线程池
以上四种方式的对比:
方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致 服务器资源耗尽。
方式 4:通过如下两种方式初始化线程
线程池实战
(将多线程异步任务都交给线程池执行)
// 创建一个线程池,整个系统只有一个
ExecutorService service=Executors.newFiexedThreadPool(3);
//
service.excute() // 不返回直接执行
service.submit() // 获取任务返回值
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory,
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一 个异步调用可能会依赖于另一个异步调用的执行结果。
线程池
一. 线程池的创建
- 七大参数
-
corePoolSize 池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut
-
maximumPoolSize 池中允许的最大的线程数
-
keepAliveTime (释放空闲的线程) 当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放, 最终线程池维持在 corePoolSize 大小
-
unit 时间单位
-
workQueue
阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize 大小,就会放在这里等待空闲线程执行 -
threadFactory 创建线程的工厂,比如指定线程名等
-
handler 拒绝策略,如果线程满了,线程池就会使用拒绝策略
工作顺序:
- 线程池创建,准备好core数量的核心线程,准备接受任务
- core满了,任务进入阻塞队列等待,等core空闲时,从阻塞队列中获取任务到core中执行
- 阻塞队列满了,开启新的线程,最大只能到达maximumPoolSize
- 线程数大于核心线程数时,空闲线程会在keepAliveTime时间后自动销毁
- 线程数到达了max数量,若还有新任务进来执行拒绝策略(handler)默认使用的是丢弃策略AbortPolicy
拒绝策略实现类:
常见的几种线程池
● newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若 无可回收,则新建线程。
● newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
● newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
● newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
面试题:
一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的;
先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个
被安排上了。剩下 30 个默认拒绝策略
为什么要使用异步编排:
异步编排(CompletableFuture ,获取异步结果进行编排)
在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的
Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以
通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。
1. 创建异步对象
CompletableFuture 提供四个静态方法来创建一个异步操作 (异步任务有返回值: supplyAsync() 异步回调 )
CompletableFuture.runAsync(()->{
System.out.println("当前线程:"+ Thread.currentThread().getId());
int i=10/2;
System.out.println("运行结果: "+ i);
},executor);
2. 计算完成时的回调方法
// 异步任务有返回值 CompletableFuture为Future的实现类
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果: " + i);
return i;
}, executor).whenComplete((res,exception)->{
System.out.println("成功完成结果:"+res);
System.out.println("异常是:"+exception);
}).exceptionally(throwable -> {
// 感知异常同时返回自定义信息
return 10;
});
Integer integer = future.get();
// 5.5.方法执行完成后的处理
CompletableFuture<Integer> future6 = future3.thenApplyAsync((s -> 1 / 0), executor).handle((result, exception) -> {
if (exception == null) {
return result;
}
System.out.println("handle处理异常:" + exception);
return 1;
});
System.out.println("handle处理返回结果:" + future6.get());
1、runXxxx 都是没有返回结果的,supplyXxx 都是可以通过future的get获取返回结果的
2、可以传入自定义的线程池,否则就用默认的线程池;
3、handle 方法即可获取到线程操作后的结果和异常,也能进行后续处理,返回自定义的值
3. 线程串行化方法 ( thenRun() )
● Function T:上一个任务返回结果的类型 U:当前任务的返回值类型
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果: " + i);
return i;
}, executor).thenApply((res)->{
return 22;
});
Integer integer = future.get();
System.out.println("end。。。。。"+integer);
组合执行线程任务
- 两任务都执行后再执行的操作
● thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
● thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有 返回值
● runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后, 处理该任务
// 5.6.2.二者都要完成,组合【获取前两个任务返回值,自己无返回值】
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10 / 2;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "hello";
}, executor);
CompletableFuture<Void> future03 = future01.thenAcceptBothAsync(future02,
(result1, result2) -> {
System.out.println("任务3执行");
System.out.println("任务1返回值:" + result1);
System.out.println("任务2返回值:" + result2);
}, executor);
// 5.6.1.二者都要完成,组合【不获取前两个任务返回值,且自己无返回值】
CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1执行");
return 10 / 2;
}, executor);
CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2执行");
return "hello";
}, executor);
CompletableFuture<Void> future03 = future01.runAfterBothAsync(future02, () -> {
System.out.println("任务3执行");
}, executor);
- 当两个任务中,任意一个 future 任务完成的时候,执行任务
● applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
● acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
● runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返 回值。
3. 多任务组合
● allOf:等待所有任务完成
● anyOf:只要有一个任务完成