异步与线程池
异步:在java 中都是以Thread,开启线程的方式进行的。
第一种:继承Thread
运行结果:
第二种:实现Runnable 接口:
运行效果:
第三种:Callable接口,配合FutureTask 进行使用
它的泛型就是返回值的类型
FutureTask 的本质就是一个Runnable
main():
运行结果:
Callable 好处:能拿到异步线程的结果。但是它是一个阻塞等待。只有等拿到返回结果以后,才能
想让Runnable 有返回值也是可以的。
如果FutureTask 中传递的是Runnable 接口的类,可以再穿一个类型的值(result),这样让返回值就是让result 接收。实现Runnable 也有了返回值。
第四种:线程池:给线程池直接提交任务,就能像上面一样开启线程执行。
前三种方式在业务代码中都不用,因为一个请求就new Thread(() -> sout(“XXX”)).start()。那么如果有100W 个请求,那这样很快就会耗尽系统资源。所以要用线程池的方式。
将所有的多线程的异步任务交给线程池。比如线程池中只有50 个线程来处理所有的异步请求,如果数量不够处理的话就等待有线程空闲才来进行处理,达到了资源的可控。
我们不能任务一过来就new 一个线程池,而是要保证一个系统中只有一两个线程池。
创建线程池,指定包含的线程数。
submit():执行线程任务有返回值。
execute():执行线程任务无返回值。
创建线程池的方式:
- Executors
- 原生创建线程池的方式:
ThreadPoolExecutor(): 中的接口就是ExecutorService。Executors的接口也是,所以线程池指得就是ExecutorService
关于ThreadPoolExecutor() 的构造方法:7 大参数
面试:一个线程池 core 7, max 20, queue:50。100并发进来怎么分配的。
100 个并发进来,7 个会立即得到执行。50 个会进入阻塞队列。因为此时队列已经满了,所以现在可以多开线程了,但只能再多开 13个进行执行。所以此时已经有70 个线程已经安排上了。剩下的30 个使用拒绝策略执行(一般都是丢弃这30 个线程的策略。如果不想抛弃还要执行的话,将拒绝策略修改成:CallerRunsPolicy:该策略会直接调用线程的run(),以同步的方法进行执行。只有new Thread(XX).start() 才会异步执行线程。)
Executors: 能创建4 中线程池
当异步任务与异步任务之间有相互调用的关系,那么就要编排好这个关系。
使用CompletableFuture
如何使用:
创建启动异步任务:
1.1
1.2
当异步线程任务执行完时,可以调用以下方法。
whenComplete(reslut,exception): 当线程任务完成以后执行这个方法。
结合了whenComplete()和exceptionally()
线程串行化:
当一个异步任务执行完以后才能执行另一个异步任务,这就叫串行化。
thenRun():就是当一个异步线程执行完以后调用该方法,再用这个线程去执行thenRun()中调用的线程任务。
thenRunAsy():就是当一个异步线程执行完以后调用该方法,再开一个新线程去执thenRunAsy()中调用的线程任务。
thenRun(): 不需要接收上一个异步线程的结果。
thenAccept():需要接收上一个异步线程的结果。但当前线程任务没有返回值。
当我们即要接收上一个异步线程的结果,还要让别人感知我们的结果时(当前线程任务也有返回值),就要用thenApply(…)
两组任务-都要完成。两组任务都完成以后才能往下执行。
执行完当前任务和other 的异步任务处理完以后,才能执行Runnable。
两个有一个任务完成即可执行当前任务:
不接受上一个完成的异步任务的结果
接受上一个异步任务的结果
接受上一个异步任务的结果,并把自己的结果也返回
多任务组合:
如果不指定线程池,则默认都使用main 线程。即都没开新线程出来。都是在一条线程上执行
allOf.get() : 要等三个异步线程执行完才能使个get() 拿到他们的数据。所以它也是阻塞方法。
anyOf.get(): 有任何一个异步线程执行完就能使get() 拿到数据了。