1、定义
让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现就是线程池,也体现了经典设计模式中的享元模式(重用对象)。
例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果为每位客人都配一名专属的服务员,那么成本就太高了(对比另- 种多线程设计模式: Thread-Per-Message 来一个消息创建一个线程)
注意,不同任务类型应该使用不同的线程池,这样能够避免饥饿,并能提升效率
例如,如果-一个餐馆的工人既要招呼客人(任务类型A),又要到后厨做菜(任务类型B)显然效率不咋
地,分成服务员(线程池A)与厨师(线程池B)更为合理,当然你能想到更细致的分工
2.饥饿
固定大小线程池会有饥饿现象
- 两个工人是同一一个线程池中的两个线程
- 他们要做的事情是:为客人点餐和到后厨做菜,这是两个阶段的工作
- 客人点餐: 必须先点完餐,等菜做好,上菜,在此期间处理点餐的工人必须等待
- 后厨做菜: 没啥说的,做就是了
- 比如工人A处理了点餐任务,接下来它要等着工人B把菜做好,然后上菜,他俩也配合的蛮好
- 但现在同时来了两个客人,这个时候工人A和工人B都去处理点餐了,这时没人做饭了,死锁
饥饿现象
饥饿现象-解决:
不同的任务类型调用不同的线程类型
3.创建多少线程池合适
1. 过小会导致程序不能充分地利用系统资源、容易导致饥饿
2. 过大会导致更多的线程上下文切换,占用更多内存
3.1 CPU密集型运算
(代码为数据分析,大量使用cpu)
通常采用cpu核数+ 1能够实现最优的CPU利用率,+1 是保证当线程由于页缺失故障(操作系统)或其它原因导致暂停时,额外的这个线程就能顶上去,保证CPU时钟周期不被浪费
3.2 I/O密集型运算
(web应用程序)
CPU不总是处于繁忙状态,例如,当你执行业务计算时,这时候会使用CPU资源,但当你执行I0操作时、远程RPC调用时,包括进行数据库操作时,这时候CPU就闲下来了,你可以利用多线程提高它的利用率。
经验公式如下
线程数 = 核数 + 期望 CPU 利用率 + 总时间(CPU计算时间 + 等待时间) / CPU 计算时间