一、设置线程池的大小
考虑因素
1、计算密集型(可考虑设置线程池大小为:cpuNum+1)、IO密集型
2、任务等待时间、任务计算时间两者的比值
3、其它:内存、文件句柄、套接字句柄、数据库连接等
线程池大小 = cpuNum*目标cpu使用率*(1+任务等待时间/任务计算时间)
0<=目标cpu使用率<=1
获得CPU个数:int cupNum = Runtime.getRuntime().availableProcessors();
二、线程池的原理
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
1、corePoolSize:基本大小、目标大小(没有任务执行时,线程池的大小)
- 只有工作队列满了才会创建超过corePoolSize数量的线程;
- ThreadPoolExecutor创建初期,线程不会立即启动,等到有任务创建时才会启动,除非调用prestartAllCoreThreads方法。
- allowCoreThreadTimeOut方法:通过这个方法使线程池中所有线程超时;应用:大小有限的线程池,希望在线程池在没有任务的情况下消灭所有线程,可启用该特性,并将corePoolSize设为零。
2、maximumPoolSize:最大大小;可同时活动的线程数量上限
3、keepAliveTime:存活时间,如果线程的空闲时间超过了存活时间,将会标记为可回收。(控制的是maximumPoolSize-corePoolSize部分的线程)
4、unit:单位
5、workQueue:工作队列
- FIFO无界队列:LinkedBlockingQueue
- FIFO有界队列:ArrayBlockingQueue
- SynchronousQueue:
-
1、要想把任务放入队列中,必须有另一个线程正在等待接收这个元素。否则创建新线程或执行饱和策略。 2、任务会直接移交给执行它的线程,而不是放入队列中 3、所以只有线程池是无界或可拒绝时SynchronousQueue才有实际价值
- PriorityBlockingQueue:可根据优先级安排任务
6、threadFactory:线程工厂
7、handler:饱和策略
- 终止:AbortPolicy(默认):抛异常RejectedExecutionException
- 抛弃:discard:抛弃下一个将被执行的任务,然后尝试重新提交新任务
- 抛弃旧的:DiscardOldest抛弃下一个将被执行的任务,然后尝试重新提交新任务。
- 调用者运行:caller-runs:下一个任务会在调用execute时在主线程中执行。在这个过程中,新来的任务会保存在TCP层的队列中。如果继续有任务过来,TCP层队列满了仍然会抛出异常。
8、扩展
- beforeExecute
- afterExecute
- terminated:线程池完成关闭操作时调用