Java多线程-线程池与Executor框架
线程池概念
1.现有问题:
线程是宝贵的内存资源,单个线程约占1MB空间,过多分配易造成内存溢出.
频繁的创建及销毁线程会增加虚拟机回收频率,资源开销,造成程序性能下降.
线程池:
线程容器,可设定的线程分配的数量上限.
将预先创建的对象存入池中,并重用线程池中的线程对象.
避免频繁的创建和销毁.
使用线程池的好处
线程池提供了一种限制和管理资源(包括执行一个任务). 每个线程池还维护一些基本统计信息,例如已完成任务的数量. 这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:
降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗.
提高响应速度,当任务到达时,任务可以不需要的等到线程创建就能立即执行.
提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.
线程池原理
将任务提交给线程池,由线程池分配线程,运行任务,并在当前任务结束后服用线程.
Executor框架结构
任务:执行任务需要实现的Runnable接口或Callable接口,Runnable接口不会返回结果,但是Callable接口可以返回结果
线程池:
Executor:线程池的顶级接口,
ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码,
Executors工厂类:通过此类可以获得一个线程池,
以下为创建线程的三种方式,但都不建议使用,后面会讲解推荐使用的方式.
异步计算的结果:Future接口以及Future接口的实现类FutureTask类. 当我们把Runnable接口或Callable接口的实现类提交(调用submit方法)给ThreadPoolExecutor或ScheduledThreadPoolExecutor时,会返回一个FutureTask对象.
//源码实现
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
Future
Future是一个接口,代表了一个异步计算的结果.接口中的方法用来检查计算是否完成、等待完成和得到计算的结果当计算完成后,只能通过get()方法得到结果,get方法会阻塞直到结果准备好了.如果想取消,那么调用cancel()方法.其他方法用于确定任务是正常完成还是取消了.
ThreadPoolExecutor详解
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
corePoolSize - 线程池核心池的大小.
maximumPoolSize - 线程池的最大线程数.
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间.
unit - keepAliveTime 的时间单位.
workQueue - 用来储存等待执行任务的队列.
threadFactory - 线程工厂.
handler - 拒绝策略.
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常. (默认)
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常.
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程).
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务.
线程创建过程为:首先根据来的任务数量,创建线程,当到达corePoolSIze后,创建wokeQueue等待队列,当队列满了之后,创建线程最大到maximusPollSize去处理.