目录
为什么引入线程池
创建过多线程带来的问题:
- 消耗内存资源
- CUP的开销(上下文切换)
所以引入了线程池的概念
线程池的逻辑就是提前创建好若干个线程,放进一个容器里(HashSet),如果有任务,就把这个任务分配给线程池中的线程来执行。线程执行完任务不会销毁,而是就是等待任务的分配。
线程池的好处
- 避免频繁创建销毁线程带来的性能开销(复用线程)
- 合理设置线程大小避免出现资源瓶颈(控制资源数量)
Java中提供的线程池
//固定线程数 Executors.newFixedThreadPool(3); //单个线程 Executors.newSingleThreadExecutor(); //带缓存的 Executors.newCachedThreadPool(); //可以延期执行 Executors.newScheduledThreadPool(3);
自定义线程池
核心参数
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数
- keepAliveTime 临时线程的存活时间
- unit 存活时间单位
- workQueue 阻塞队列
- threadFactory 创建线程的线程工厂
- handler 拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
拒绝策略
- AbortPolicy 抛出异常
- CallerRunsPolicy 直接调用线程的run方法
- DiscardOldestPolicy 把阻塞队列中的的任务poll,然后再把添加的任务excutor
- DiscardPolicy 不做任何处理
- 自定义拒绝策略,继承RejectedExecutionHandler重写他的方法
如何设置线程池的大小
- CPU密集型(大量的使用CPU资源) ->保持和CUP核心数一致(减少上下文切换)
- IO密集型(大量的IO处理) -> 可以根据实际情况多设置一些
继承线程池重写他的before和after方法可以对任务进行监控
execute和submit的区别
execute无返回值
submit有返回值Future
线程池的执行流程
通过excutor执行任务,首先会判断当前线程是否小于核心线程数,如果小于就会创建个Woker,然后放进hashSet容器进行存储。然后调用woker.star来启动线程。线程会调用任务的run方法来执行业务逻辑,,执行完一个,线程会自旋的去阻塞队列中take阻塞式的拿任务并执行。如果阻塞队列放满了,会判断当前线程是否小于最大线程数,如果小于就会继续创建woker线程,如果已经大于最大线程数了,就会执行拒绝策略。我们可以通过继承拒绝策略的类来自定义拒绝策略。
图解