为什么使用线程池?
线程池主要目的是为了重复利用线程,提高系统效率 。Thread是一个重量级的资源,创建、启动以及销毁都是比较耗费系统资源的,因此对线程的重复利用一种是非常好的程序设计习惯,加之系统中可创建的线程数量是有限的,线程数量和系统性能是一种抛物线的关系,也就是说当线程数量达到某个数值的时候,性能反倒会降低很多,因此对线程的管理,尤其是数量的控制更能直接决定程序的性能。 |
---|
--------------------------------------------------------------------读书笔记摘自书名:Java高并发编程详解:多线程与架构设计 作者:汪文君 |
Java中的线程池是运用场景最多的并发框架 ,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处 。 |
---|
第一:降低资源消耗 。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 |
第二:提高响应速度 。当任务到达时,任务可以不需要等到线程创建就能立即执行。 |
第三:提高线程的可管理性 。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。 |
--------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明 |
线程池原理 && 线程池处理流程
线程池的创建
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}
参数 | 功能 |
---|---|
corePoolSize 核心线程数 线程池的大小 | the number of threads to keep in the pool , even if they are idle. |
maximumPoolSize 线程池最大数量 | the maximum number of threads to allow in the pool. |
keepAliveTime | when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. |
unit | 可选的单位有天(DAYS )、小时(HOURS )、分钟(MINUTES )、毫秒(MILLISECONDS )、微秒(MICROSECONDS ,千分之一毫秒)和纳秒(NANOSECONDS ,千分之一微秒)。 |
BlockingQueue<Runnable> workQueue | the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.线程池任务队列 |
threadFactory 创建线程的工厂 | the factory to use when the executor creates a new thread |
RejectedExecutionHandler handler 拒绝策略 | the handler to use when execution is blocked because the thread bounds and queue capacities are reached 当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。 线程池饱和策略 |
向线程池提交任务
public interface Executor {void execute(Runnable command);
}
public interface ExecutorService extends Executor {Future<?> submit(Runnable task);
}
execute && submit |
---|
execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功。 |
executorService.execute(new RunnableTask("任务")); |
submit() 方法用于提交需要返回值的任务。线程池会返回一个future类型的对象。通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。 |
executorService.submit(new RunnableTask("任务")); |
Future<String> future = executorService.submit(new CallableTask("name")); |
关闭线程池
shutdown && shutdownNow |
---|
executorService.shutdown(); |
executorService.shutdownNow(); |
它们的原理是遍历 线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,所以无法响应中断的任务可能永远无法终止 。但是它们存在一定的区别,shutdownNow 首先将线程池的状态设置成STOP ,然后尝试停止所有的正在执行 或暂停 任务的线程,并返回等待执行 任务的列表,而 shutdown 只是将线程池的状态设置成SHUTDOWN 状态,然后中断所有没有正在执行 任务的线程。 |
只要调用了这两个关闭方法中的任意一个,isShutdown 方法就会返回true 。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用 isTerminaed 方法会返回true 。至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法。 |
合理地配置线程池
-----------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明