Java线程池简单介绍
Java 线程池介绍
一、线程池
线程池,从字面含义来看,是指管理一组同构工作线程的资源池。线程池是与工作队列密切相关的,其中在工作队列中保存了所有等待执行的任务。工作者线程的任务很简单:从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。
跟数据库连接池相似,如果每一次操作,都要新建一个线程(连接)然后销毁,会对系统资源产生巨大开销,通过线程池(连接池)对线程(连接)进行管理,让一些线程(连接)一直存在,既可以减少新建销毁的资源浪费,也可以提高系统的响应性。
二、Java 中的线程池类
在 Java1.8 中,已经在 Executors 中封装好了几种线程池的静态实现方法,这些方法都是调用统一的线程池构建函数来构建这几种线程池,这几种线程池分别是:
- newCacheThreadPool:创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中。
- newFixedThreadPool:创建一个可重用的、具有固定线程数的线程池。
- newSingleThreadExecutor:创建一个只有单线程的线程池,它相当于调用 newFixedThreadPool 时设置大小为 1.
- newScheduledThreadPool:创建具有指定线程数的线程池,它可以在指定延迟后执行线程任务。
- newSingleScheduledThreadPool:创建只有一个线程的线程池,它可以在指定延迟后执行线程任务。
- newWorkStealingPool:创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争。
除了具有延迟功能的线程池返回的是 ScheduledExecutorService 类外,其余方法的返回值都是接口 ExecutorService 。
下面来放张图看看 Java 关于线程的脉络:
左边是线程池相关的,右边的是 Future 相关的。Future 是对线程的一种包装,可以通过 Future 获取线程的一些信息,比如线程是否运行,线程返回值等。
我们分析一下 ScheduledExecutorService 和 ExecutorService 中的一些方法,这些方法只列出了包含所有参数的方法,能省略的参数可以自行查看 API 文档:
ExecutorService
代表尽快执行线程的线程池,只要线程池中有空闲线程,就立即执行线程任务:
- <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit):执行给定的任务集合,并在所有任务完成或超时后(即 unit 参数)返回所有任务的 Future 集合,超时未完成的任务将会被取消,如果任务抛出异常也当作已完成。
- <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) :执行给定的任务集合,一旦有任务完成或抛出异常,就返回完成任务的结果(抛出异常无结果),超时与上面方法一致。
- boolean awaitTermination(long timeout, TimeUnit unit):阻塞当前进程直到所有任务完成或超时或抛出异常。
- boolean isTermination():如果所有任务在关闭后都已完成,则返回 true。注:除非先调用 shutdown 或 shutdownNow,否则 isTerminated 永远不会为真。
- void shutdown():启动有序关闭,在该关闭中执行先前提交的任务,但不接受任何新任务。
- List<Runnable> shutdownNow():尝试停止所有正在执行的任务,暂停正在等待的任务的处理,并返回正在等待执行的任务的列表。任何无法响应中断的任务都可能永远不会终止。
- boolean isShutdown():如果此执行程序已关闭,则返回 true。
- Future<?> submit(Runnable task):提交一个 Runnable 任务以执行并返回一个表示该任务的 Future。成功完成后,Future 的 get 方法将返回 null。
ScheduledExecutorService
代表可在指定延迟后或周期性地执行线程任务地线程池
- <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit):指定 callable 任务将在 delay 延迟后执行。
- ScheduledFuture<V> schedule(Runnable command, long delay, TimeUnit unit):指定 command 任务将在 delay 延迟后执行。
- ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):指定 command 任务将在 delay 延迟后执行,而且以设定频率重复执行。也就是说,在 initialDelay 后开始执行,依次在 initialDelay+period,initialDelay+2*period 处重复执行,以此类推。
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):创建并执行一个在给定初始延迟后首次启用的定期操作,随后在每一次执行终止和下一个执行开始之间都存在给定的延迟。如果任务在任一次执行时遇到异常,就会取消后续执行,否则,只能通过程序来显式取消或终止该任务。
三、自定义线程池的创建
我们可以看一下线程池的构造函数,在 ThreadPoolExecutor 类中一共有以下四种构造方法:
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
参数介绍:
- corePoolSize - 即使线程池空闲,也要保留的线程数量。
- maximumPoolSize - 线程池中允许的最大线程数。
- keepAliveTime - 当线程数大于内核数(即线程池中的线程大于 corePoolSize)时,这是多余的空闲线程将在终止之前等待新任务的最长时间,时间到后多余线程会销毁。如果调用了 allowCoreThreadTimeOut(boolean) 方法,传入 true,则核心线程池数量也会像普通线程一样被逐渐关闭,直到线程池大小为 0.
- unit - keepAliveTime 参数的时间单位。
- workQueue - 在执行任务之前用于保留任务的阻塞队列。此队列将仅保存 execute 方法提交的 Runnable 任务。
- threadFactory - 执行程序创建新线程时要使用的工厂。
- handler - 因达到线程池最大数量和队列容量而被阻止执行时使用的处理程序,也称为拒绝策略。
阻塞队列:ArrayBlockingQueue 和 PriorityBlockingQueue 使用较少,一般使用 LinkedBlockingQueue 和 Synchronous。线程池的排队策略与 BlockingQueue 有关,详情可参考Java 并发编程:阻塞队列
拒绝策略:
拒绝策略是 ThreadPoolExecutor 中的常量:
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
Java 中封装好的线程池就是调用上面的构造函数来创建的,不过他设置好了对应的参数,而延迟线程池的构造方法的参数与普通线程池的构造方法的参数相似,这里就不再重复。
以上就是线程池的简单介绍和创建。