利用Executors创建不同的线程池满足不同场景的需求
线程池的创建方式: Java基础二、多线程
1、Fork/Join框架
把大任务分割成若干小任务并行执行,最终汇总每个小任务结果后得到大任务结果的框架。
2、Work-Stealing算法: 某个线程从其他队列里窃取任务来执行。
当某个线程闲置后,可以窃取其它线程池的任务来执行。
3、为什么要使用线程池
1) 降低资源的消耗。 (利用重复利用已经创建的线程,以降低线程创建和销毁造成的消耗)
2) 提高线程的可管理性
4、Executor框架
J.U.C的三个Executor接口
1) Executor接口 运行新任务的简单接口,将任务提交和任务执行细节解耦。
2) ExecutorService接口: 具备管理执行器和任务生命周期的方法,提交任务机制更完善。
subbit方法<T> Future<T> submit(Callable<T> task);
3) ScheduledExecutorService接口 支持Future和定期执行任务
4) ThreadPoolExecutor类
WorkQueue工作队列: 可以是LinkedBlockingQueue,SynchronousQueue等。
5) ThreadPoolExecutor类
里面有一个Worker类,实现了Runnable接口
构造函数中this.thread = getThreadFactory().newThread(this);
使用ThreadFactory创建线程。
ThreadPoolExecutor的构造函数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
corePoolSize: 核心线程数量
maximumPoolSize: 线程不够用时能够创建的最大线程数
workQueue: 任务等待队列
keepAliveTime: 线程池维护空闲线程的时间,超过等待时间,空闲线程被销毁。
threadFactory:创建新线程,Executors.defaultThreadFactory()
handler: 线程池的饱和策略
AbortPolicy: 直接抛出异常,这是默认策略
CallerRunsPolicy: 用调用者所在的线程来执任务
DiscardOldestPloicy: 丢弃队列中靠最前的任务,并执行当前任务。
DiscardPolicy: 直接丢弃任务
实现RejectedExecutionHandler接口的自定义handler
新任务提交execute执行后的判断
流程图如下:
线程池线程数量的存储
ThreadPoolExecutor类的ctl 变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
线程池的状态
RUNNING: 能接受新提交的任务,并且也能处理阻塞队列中的任务。
SHUTDOWN: 不再接受新提交的任务,但可以处理存量任务。
STOP: 不再接受新提交的任务,也不处理存量任务。
TIDYING: 所有的任务都已终止。
TERMINATED: terminated()方法执行完后进入该状态。
线程池的状态转换图
工作线程的生命周期
线程池的大小如何选定
CPU密集型: 线程数=按照核数或者 核数+1设定
I/O密集型: 线程数= CUP核数 * (1 + 平均等待时间/平均工作时间)