Java线程池(废弃)
服务器会接受大量请求(每个请求一个线程),若线程很多但是服务时间很短,则会频繁的创建/销毁线程,这会极大的降低系统效率。
为什么要使用线程池
- 降低线程创建/销毁的资源消耗
- 提高线程的可管理性(数量/分配/监控等)
利用Executors创建不同的线程池满足不同场景的需求
1 | newFixedThreadPool(int nThreads); // 指定工作线程固定数量的线程池 |
Fork/Join框架
J.U.C的三个Executor接口
java.util.concurrent;
Executor:运行新任务的简单接口,将任务提交和任务执行细节解耦
1
2
3
4
5
6// 传统线程启动方法
Thread t = new Thread();
t.start();
// Executor接口启动方法
Thread t = new Thread();
executor.execute(t);ExecutorService:具备管理执行器和任务生命周期的方法,提交任务机制更完善
ScheduledExecutorService:支持Future和定期执行的任务
ThreadPoolExecutor的构造函数
- corePoolSize:核心线程数量
- maximumPoolSize:线程不够用时能够创建的最大线程数
- workQueue:任务等待队列(任务提交时,线程数量已经大于corePoolSize,则把该任务放进workQueue等待)
- keepAliveTime:线程池维护线程允许的空闲时间,线程数量大于corePoolSize时,若没有新线程提交,则核心线程以外的线程不会被立即销毁,而是等待keepAliveTime时间再销毁。抢占的顺序不一定,看运气
- threadFactory:创建新线程,Executors.defaultThreadFactory(),优先级相同、非守护
- handler:线程池的饱和策略。当线程池满了之后的策略
- AbortPolicy:直接抛出异常,默认策略
- CallerRunsPolicy:用调用者所在的线程来执行任务
- DiscardOldestPolicy:丢弃队列中最靠前的任务,并执行当前任务
- DiskcardPolicy:直接丢弃本任务
- 也可以实现RejectedExecutionHandler接口的自定义handler
新任务提交execute执行后的判断
- 如果运行的线程少于corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;
- 如果线程池中的线程数量大于等于corePoolSize且小于maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;
- 如果设置的corePoolSize和maximumPoolSize相同,则创建的线程池的大小是固定的,这是如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程从workQueue中取任务并处理;
- 如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue也满了,则通过handler所指定的策略来处理任务。
线程池的状态
RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务
SHUTDOWN:不再接受新提交的任务,但可以处理存量任务
STOP:不再接受新提交的任务,也不处理存量任务
TIDYING:所有任务都已终止
TERMINATED:terminated()方法执行完后为该状态
工作线程的生命周期
线程池的大小如何选定
- CPU密集型:线程数= CPU核心数量+1设定
- I/O密集型:线程数 = CPU核心数量 * (1 + 平均等待时间/平均工作时间)