1.在单个线程里处理所有的请求:接受请求-处理请求
优点:逻辑简单
缺点:吞吐量低,资源利用率低,响应时间长
2.每个任务分配一个单独的线程来处理: 接受请求-创建线程-在线程里处理请求
优点:
- 主线程解除了处理请求这一负载,可以更快速地接受下一个请求,提高相应速度。
- 任务可以并行执行,进而提高吞吐率
- 当其中某个或某几个线程中的任务因为I/O等原因而阻塞时,其他线程不受影响
缺点:
- 任务处理代码必须是线程安全的,因为现在会有多个线程并发执行这些代码,使得代码复杂度提高,更易出错
- 当请求的速度超过处理的速度时,线程的数量会一直增长,可能导致资源耗尽,GC压力增大
- 创建、销毁线程开销较大,这个过程耗费的时间最终会传递到响应时间,同时对资源也是很大的消耗
- 线程数过高导致系统稳定性受到损害
多个线程可以提升吞吐量,但线程数不能无限制地增长,因此要限制线程的数量。
引入 Executor Framework
Executor接口支持多种执行策略,将任务的递交和任务的执行解耦。Executor基于生产者-消费者模型。生产者递交任务,消费者执行任务。
执行策略:
所谓的“任务递交和任务执行解耦”就是说你可以轻易地通过实现自己的Executor接口改变某个任务的执行策略。执行策略规定了what,where,when,how等问题
- 任务在哪个线程中执行
- 任务以何种方式来执行
- 允许多少个任务并发执行
- 允许多少个任务排队
- 如果因系统超负荷而需要被拒绝某个任务,那么哪个任务会被选中作为牺牲品呢?怎么把这个事件通知给应用程序呢?
- 执行任务之前或者之后需要做哪些额外工作?
线程池 对 每个任务创建一个线程 的优势:
- 减少线程创建销毁的开销。
- 提高响应速度
newFixedThreadPool:随着任务的递交创建线程池,直到达到上限后不再增加
newCachedThreadPool:当当前的线程池大小超出处理任务所需数量时,销毁一些空闲线程,另一方面,当需求增加时,再创建新的线程,对线程数量没有限制。
newSingleThreadExecutor :单线程执行器,创建单个线程来处理任务,任务保证是序列化处理的。
newScheduledThreadPool:固定大小的线程池,支持滞后的或者周期性的任务,类似于定时器。
Executor的生命周期