一.为什么使用线程池:
1.降低资源损耗。
主要指的是降低创建和销毁线程时产生的cpu资源消耗,线程池通过持续工作的线程执行不断分配的新 任务,来减少频繁的线程创建与销毁。
2.提高响应速度。
3.提高线程的可管理性。
线程是稀缺资源,频繁的创建销毁,以及没有控制的大量创建,都会影响系统的稳定性。使用线程池可以统一分配,调优,资源监控。
二.如何创建线程池:
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler);
1.corePoolSize:
线程池的基本大小,即在没有任务需要执行的时候线程池中运行的线程数量。只有在工作队列满了的情况下才会创建超出这个数量的线程。
2.maximumPoolSize:
线程池中允许的最大线程数,线程池中的当前运行线程数不会超过这个值,为了保证系统资源有控制的消耗。
3.keepAliveTime:
当线程空闲后,保留的时间。用来在任务不繁忙的时候减少运行线程数量。在单任务时间较短,任务请求较频繁的情况下,可以适当延长保留存活时间来减少系统创建和销毁线程的开销。
4.TimeUnit unit:
用来修饰keepAliveTime的时间单位,可以为纳秒,毫秒,秒,分,小时,天等TimeUnit中的静态常量。
5.BlockingQueue:
线程安全的队列,用来存放排队的任务。
6.ThreadFactory:
用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
7.RejectedExecutionHandler:
当队列和线程池都满了之后的饱和策略。默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。
三.如何调用线程池:
1.通过execute(Runnable run)方法来执行任务:
threadsPool.execute(new Runnable(){
@override
public void run(){ //code to run. }
});
但是execute()方法没有返回值,无法获得任务执行的情况信息。
2.通过submit()方法来提交执行任务:
Future future=executor.submit(harReturnValueTask);
try{
Object o=future.get();
}catch(Exeception e){
}finally{
executor.shutdown();
}
submit()方法会返回一个future对象,通过future的get()方法,可以获得任务执行情况。get()方法会阻塞直到任务执行完毕。也可以使用get(long timeout, TimeUnit unit)来设置最长阻塞时间。
四.如何关闭线程池:
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。