在计算机中,线程是稀缺资源,创建过多的线程,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:
- 降低资源消耗;
- 提高响应速度;
- 提高线程的可管理性。
Java多线程编程常用到多线程框架Executor,使用此框架可以方便、高效的对线程进行管理,我们先了解下Executor。
Executor是从Java 5开始引入的一个框架,在java.util.concurrent 包下,其内部使用了线程池机制,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,更方便管理,效率更高。
Executor框架主要包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future/FutureTask,Runnable/Callable等。
Executor接口
Executor接口是Executor框架最基本的接口,Executor框架的大部分类都直接或间接地实现了此接口。Executor接口只有一个方法:
void execute(Runnable command);
该方法接收一个Runable实例参数,用来执行一个任务,这个任务就是一个实现了Runnable接口的类。
ExecutorService接口
ExecutorService接口继承自Executor接口,它提供了多个方法用于对线程进行管理。
public interface ExecutorService extends Executor {
//依次关闭任务,在此过程中会执行先前提交的任务,但不接受任何新任务。
void shutdown();
//阻止等待中的任务启动,并试图停止当前正在执行的任务,不再接收新任务,返回处于等待队列的任务列表
List<Runnable> shutdownNow();
//判断线程池是否已经关闭
boolean isShutdown();
//如果执行关闭操作后所有任务都已完成,则返回true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。
boolean isTerminated();
//阻塞并等待被关闭任务执行完成,或发生超时,或发生中断
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
//提交一个有返回值的 Callable 任务用于执行,并返回一个表示任务的未决结果的Future。任务成功执行时,该Future的get()方法将会返回执行结果。
<T> Future<T> submit(Callable<T> task);
//提交一个 Runnable 任务用于执行,并返回一个表示该任务的Future。任务成功执行时,该Future的get()方法将会返回给定的结果 result。
<T> Future<T> submit(Runnable task, T result);
//提交一个 Runnable 任务用于执行,并返回一个表示该任务的Future。任务成功执行时,该Future的get()方法将会返回null
Future<?> submit(Runnable task);
//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
//执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException;
//执行给定的任务,给定的任务中任何一个任务完成时,则返回其结果。一旦正常或异常返回,则取消尚未完成的任务。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
//执行给定的任务,给定的超时时间内任何一个任务完成时,则返回其结果。一旦正常或异常返回,则取消尚未完成的任务。
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Executors类
Executors是Java线程池的工厂类,它提供了一系列方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
常用的创建线程池的方法:
public static ExecutorService newSingleThreadExecutor();
创建一个单线程的线程池。这个线程池用唯一的工作线程来执行任务,所有任务按照指定顺序依次执行。
public static ExecutorService newFixedThreadPool(int nThreads);
创建具有固定的线程数量的线程池。线程池中的这些线程可重用,当所有线程都处于活动状态时,再次提交的任务都会加入等待队列,等待其他线程结束,当有线程处于空闲状态时会被下一个任务复用。
public static ExecutorService newCachedThreadPool();
创建一个可缓存线程的线程池,调用execute()将重用以前构造的线程(如果有线程可用)。如果当前没有线程可用,则创建一个新线程并添加到线程池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
可以看出缓存线程池大小是不定值,根据需要会创建不同数量的线程。在使用缓存型线程池时,先查看线程池中有没有空闲线程,如果有,就复用;如果没有,新建线程并加入线程池中。缓存型线程池通常用于执行一些生存期很短的异步型任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
创建一个支持定时及周期性任务执行的线程池。此线程池中的线程可延迟执行,定时执行或固定周期执行。
CompletionService
CompletionService接口用于异步获取并行任务的执行结果。使用Future和Callable可以获取线程的执行结果,但是获取的方式是阻塞的,需要根据线程添加到线程池中的顺序依次获取,如果获取不到就会阻塞。为了解决这种情况,可以使用CompletionService非阻塞的获取任务的执行结果。CompletionService异步非阻塞的获取任务执行结果是通过队列来实现的,一个任务执行完成后就将其执行结果放到队列中,获取结果是从队列中取,先执行结束的线程的结果先被获取,避免了某个线程阻塞会影响后续线程执行结果获取的情况。
Runnable/Callable
实现了Runnable或Callable接口的类就是线程的执行类,线程业务代码要被封装在实现了Runnable或Callable接口的类中。实现了Runnable接口的线程业务类可以使用execute()方法直接执行,也可以使用ExecutorService的submit()方法提交并执行。实现了Callable接口的线程业务类,只能通过ExecutorService的submit(Callable task) 方法来提交并执行,并且返回一个Future,这个Future表示任务的未决执行结果。
线程池
表示线程池的类有两个:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor,Executors类创建的线程池就属于这两个类中的一个。
ThreadPoolExecutor:单线程线程池、可缓存的线程池、固定数目线程的线程池都是ThreadPoolExecutor类。
ScheduledThreadPoolExecutor: 支持定时及周期性的任务的线程池是ScheduledThreadPoolExecutor类。
示例
待补充
参考:
https://blog.csdn.net/fangqun663775/article/details/78961417
https://www.cnblogs.com/fengsehng/p/6048610.html
https://blog.csdn.net/tongdanping/article/details/79604637