-
什么是线程池,为什么要线程池
- 降低资源消耗,降低创建和销毁的资源消耗
- 提高响应速度:线程的创建时间为T1,执行时间为T2,销毁时间T3,免去T1和T3的时间
- 提高线程的可管理性
-
实现一个自己的线程池
- 线程在池子已经创建好了,并且可以保持住,要有容器保存多个线程
- 线程还要能接受外部的任务,并运行这个任务
-
线程池的创建
- ThreadPoolExecutor,jdk所有线程池实现的父类
- 各个参数含义
1> int corePoolSize:线程池中核心线程数,<corePoolSize,就会创建线程,=corePoolSize这个任务就会保存到BlockingQueue。
PrepareAllCoreThreads()方法就会一次性创建corePoolSize个线程
2> int maximumPoolSize:允许的最大线程数,BlockingQueue也满了,<maximumPoolSize时候就会再次创建新的线程
3> long keepAliveTime:线程空闲下来存活的时间,这个参数只在线程数>corePoolSize才有用
4> TimeUnit unit:存活时间单位
5> BlockingQueue<Runnable> workQueue,保存任务的阻塞队列
6> ThreadFactory threadFactory:创建线程的工厂,给新建的线程赋予名字
7> RejectExcutionHandler handler:饱和策略
AbortPolicy:直接抛出异常,默认的
CallerRunsPolicy:用调用者所在的线程来执行任务
DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
DiscardPolicy:当前任务直接抛弃
实现自己的饱和策略:实现RejectExcutionHandler接口即可
-
提交任务
- execute(Runnable command) 不需要返回
- submit(Callable callable) 需要返回
-
关闭线程池
- shutdown():设置线程池的状态,只会中断所有没有执行任务的线程
- shutdownNow():设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程
-
合理配置线程池
- 根据任务的性质来:计算密集型(cpu),IO密集型,混合型
- 计算密集型:加密,大数分解,正则。。。。Cpu数适当小一点,最大推荐:机器的Cpu核心数+1,+1为了防止页缺失。Runtime.getRuntime().availableProcessors()获取CPU核心数
- IO密集型:读取文件,数据库连接,网络通讯。线程数适当大一点。机器的cpu核心数*2
- 混合型:尽量拆分。当IO密集型远大于计算密集型,拆分意义不大;当IO密集型远约等于计算密集型,非常有必要
- 队列的选择应该使用有界;无界队列可能会导致内存溢出OOM
-
预定义的线程池(最好不要用,它们的阻塞队列都使用了无界队列,饱和策略不适合生产任务,尽量使用ThreadPoolExecutor)
- FixedThreadPool:创建固定数量,适用于负载较重的服务器,使用了无界队列
- SingleThreadExecutor:创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用了无界队列
- CachedThreadPool:会根据需要来创建新线程,适用于很多短期异步任务的程序
- WorkStealingPool(jdk7以后):基于ForkJoin
- ScheduledThreadPool:适用于定期执行任务的连接池,Timer不建议使用,异常处理的不好。
1> newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务
2> newScheduledThreadPool:可以包含多个线程,线程执行周期任务,适度控制后台线程数量的时候
3> 方法说明
a,schedule:只执行一次,任务还可以延迟执行
b,scheduleAtFixedRate:提交固定时间间隔的任务,两个任务头之间的间隔是固定的
c,scheduleWithFixedDelay:提交固定延时间隔的任务,前一个任务的结尾和后一个任务的头之间的时间间隔是固定的
4> 建议在提交给ScheduledThreadPoolExcutor的任务要catch异常 -
Executor类图
Executor类图.png -
Executor的基本使用图
Executor的基本使用.png
-
CompletionService:线程池中的线程合理管理
网友评论