1:将低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗,如果你不使用线程池,而是自己去创建线程,比如要100个线程你都要去创建的话,内存也吃不消,这就大大消耗了内存,既然你创建消耗了内存,所以你线程执行完销毁也是需要消耗内存的.
2:提高响应速度:当任务达到时,认为可以不需要等待线程创建就能立即执行
3:提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,这是指消耗cpu,因为cpu要分配时间片给线程执行,还会降低系统的稳定性,线程多了,出错的概率就越大,并发本来就比较难把握.
当我们向线程池提交一个任务之后,线程池是如何处理这个任务呢?线程池处理线程的流程如下:
1:线程池判断核心线程池里的线程是否都在执行.如果不是,则创建一个新的工作线程来执行,如果核心线程池里的都在执行任务,则进入第二步:
2:线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里,如果工作队列满了,则进入第三步
3:判断线程池的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务,如果已经满了,则交给饱和策略来处理这个任务.
现在画一个流程图对线程池的流程做个梳理:
在java中线程池用ThreadPoolExecutor类表示,要实例化这个对象呢?有二种方法,一种是直接new,一种是通过jdk给我们提供的工具类Executors,Executors是单例的
现在看下ThreadPoolExecutor类的构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数说明:
1:corePoolSize:线程池中核心线程数,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行任务也会创建线程,等到需要执行的任务大于线程池中的核心线程数,就不会再创建,如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程
2:maximumPoolSize:线程池最大线程数,也就是线程池允许创建的最多线程数,如果队列满了,并且已创建的线程数小于线程池设置的最多线程数,则线程池会再创建新的线程执行任务,注意,如果使用了无界的任务队列这个参数就不起什么作用.
3:keepAliveTime:(线程活动保持的时间)线程池的工作线程空闲后,保持存活的时间,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率
4:unit(线程活动保持时间的单位):可选的单位有天DAYS,小时HOURS,分钟MINUTES,毫秒MILLISECONDS,微妙MICROSECONDS,千分之一毫秒和纳秒NANOSECONDS=千分之一微妙
5:workQueue(阻塞队列),用于保存等待执行任务的阻塞队列,jdk提高了好几种阻塞队列,这个会等下细讲6:threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置一个更有意义的名字,一般很少使用.
7:handler(饱和策略),当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务,这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常,在jdk1.5中线程池框架提供了4种策略:AbortPolicy:直接抛出异常CallerRunsPolicy:只用调用者所在线程来执行任务DisCardOldestPolicy:遗弃队列里最近的一个任务,并执行当前任务.DiscardPolicy:不处理,直接遗弃,来自网上的一张图:
线程池执行二种方式
当我们创建好了线程池对象后,就可以向线程池提交任务,提交任务有种方式,分别有execute()和submit(),
execute()方法提交任务不需要返回值,所以无法判断任务是否被线程池执行成功