线程异步编排-线程池
1.初始化线程的4种方式
1).继承Thread接口
2).实现Runnable接口
3).实现Callable接口 + FutureTask(可以拿到返回结果,可以批量处理) JDK1.5加入
4).线程池[将所有的多线程任务都交给线程池执行] Future可以获取到异步结果
方式1和方式2:主线程无法获取线程的运算结果,不适合当前场景
方式3:主线程可以获取线程的运算结果,但是不利于控制服务器中的线程资源,可以导致服务器资源耗尽
方式4:通过如下两种方式初始化线程池
Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit,workQueue,threadFactory,handler);
//线程池控制资源,性能稳定
public static ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Runnable01());
通过线程池性能稳定,也可以获取执行结果,并捕获异常,但是,在业务复杂的情况下,一个异步调用可能会依赖于另一个异步调用的执行结果
ThreadPoolExecutor executor=new ThreadPoolExecutor();
//七大参数
corePoolSize 核心线程数[一直存在,除非(allowCoreThreadTimeOut)]:线程池创建好以后就准备就绪的线程数量,就等待来接收异步任务去执行
5个 Thread thread = new Thread(); thread.start();
maximumPoolSize 最大线程数:控制资源并发
keepAliveTime 存活时间,如果当前线程数量大于核心线程数释放空闲线程(maximumPoolSize-corePoolSize),只要线程空闲大于指定的keepAliveTime
unit 时间单位
BlockingQueue<Runnable> workQueue 阻塞队列:如果任务有很多,就会将目前多的任务放在队列中里面只要有线程空闲,就会去队列里面取出新的任务继续执行
threadFactory 线程的创建工厂
RejectedExecutionHandler handler 如果队列满了,按照我们指定的拒绝策略拒绝执行任务
//new LinkedBlockingQueue<>(10000) 默认是Integer的最大值 指定线程数量/压力测试得到
ThreadPoolExecutor executor=
new ThreadPoolExecutor(5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
运行流程:
1.线程池创建,准备好core数量的核心线程,准备接受任务
2.新的任务进来,用core准备好的空闲线程执行
(1)core满了,就将再进来的任务放入阻塞队列中,空闲的core就会自己去阻塞队列获取任务执行
(2)阻塞队列满了,就直接开新线程执行,最大只能开到max指定的数量
(3)max都执行好了,Max-Core数量空闲的线程会在keepAllveTime指定的时间后会自动销毁,最终保持到core大小
(4)如果线程数开到max的数量,还有新任务进来,就会使用reject指定的拒绝策略进行处理
如果不想抛弃还要执行,CallerRunsPolicy拒绝策略
2.常见的4种线程池[Executors]
newCacheThreadPool core是0,所有都可回收
创建一个可缓存线程池,如果线程长度超过处理需要,可灵活回收空闲线程,若无法回收,则创建新的线程
newFixedThreadPool 固定大小,coremax,都不可回收
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool 定时任务的线程池
创建一个定长线程数,支持定时及周期性任务执行
newSingleThreadExecutor 单线程的线程池,后台从队列里面按顺序挨个执行任务
串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行
3.开发中为什么使用线程池
降低资源的损耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的资源消耗
提高响应速度
因为线程池中的线程数没有超过线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销,无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
异步编排 CompletableFuture JDK1.8加入
业务场景
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必须花费更多的时间
//获取sku的基本信息 0.5s
//获取sku的图片信息 0.5s
//获取sku的促销信息 1s
//获取spu的所有销售属性 1s
//获取规格参数组以及组下的规格参数 1.5s
//spu详情 1s
假如商品详情页的每个查询,需要如下标往的时间才能完成
那么,用户需要6.5s后才能看到商品详情页的内容。很显然是不能接受的。
如果有多个线程同时完成这6步操作,也许只需要1.55 即可完成响应。
1.创建异步对象
CompletableFuture 提供了四个静态方法来创建一个异步操作
static CompletableFuture<void> runAsync(Runnable runnab1e)
public static CompletableFutur e<Void> runasync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyasync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync (Supplier<U> supplier, Executorexecutor )
1、runXxox都是没有返回结果的,supplyXx 都是可以获取返回结果的
2、可以传入自定义的线程池,否则就用默认的线程池;
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("开始.........");
/*CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("当前线程ID: " + Thread.currentThread().getId());
int count = 10 / 2;
System.out.println("运行结果: " + count);
}, executor);*/
//不接受入参,只有返回值
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程ID: " + Thread.currentThread().getId());
int count = 10 / 2;
System.out.println("运行结果: " + count);
return count;
}, executor);
System.out.println("结束........."+future.get());
}
2.计算完成时回调方法
public CompletableFuture<T> whenC omplete(BiConsumer<? super T,7 super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,7 super Thr owable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<