前言
在我们的日常开发中,难免会使用到线程,部分还会用到多线程并发问题。我们知道,线程的创建和释放,需要占用不小的内存和资源。如果每次需要使用线程时,都new 一个Thread的话,难免会造成资源的浪费,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。不利于扩展,比如如定时执行、定期执行、线程中断,所以很有必要了解下ExecutorService的使用。
ExecutorService是Java提供的线程池,也就是说,每次我们需要使用线程的时候,可以通过ExecutorService获得线程。它可以有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行、定期执行、单线程、并发数控制等功能,也不用使用TimerTask了。
1.ExecutorService的创建方式
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
所有线程池最终都是通过这个方法来创建的。
corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。
maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。
keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。
unit : 时间单位,TimeUnit.SECONDS等。
workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。
threadFactory : 线程工程,用于创建线程。
handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序
2.线程池的类型
2.1 可缓存线程池
ExecutorService cachePool = Executors.newCachedThreadPool();
看看它的具体创建方式:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式。它只适用于生命周期短的任务。
2.2 单线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
顾名思义,也就是创建一个核心线程:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
只用一个线程来执行任务,保证任务按FIFO顺序一个个执行。
2.3 固定线程数线程池
Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
也就是创建固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行。
2.4 固定线程数,支持定时和周期性任务
ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
可用于替代handler.postDelay和Timer定时器等延时和周期性任务。
public ScheduledFuture schedule(Runnable command,
long delay, TimeUnit unit);
public ScheduledFuture scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
scheduleAtFixedRate和sheduleWithFixedDelay有什么不同呢?
scheduleAtFixedRate:创建并执行一个在给定初始延迟后的定期操作,也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后下一个任务执行,接着在 initialDelay + 2 * period 后执行,依此类推 ,也就是只在第一次任务执行时有延时。
sheduleWithFixedDelay:创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟,即总时间是(initialDelay + period)*n
2.4 手动创建线程池
private ExecutorService pool = new ThreadPoolExecutor(3, 10,
10L, TimeUnit.SECONDS,
new LinkedBlockingQueue(512), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
可以根据自己的需求创建指定的核心线程数和总线程数。
3. 如何终止一个周期性任务呢?
直接上源码你就懂了:
public final class LgExecutorService {
private ConcurrentHashMap futureMap = new ConcurrentHashMap<>();
private ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5);
private LgExecutorService() {
}
private static final class InnerExecutorService {
private static final LgExecutorService INSTANCE = new LgExecutorService();
}
public static LgExecutorService getInstance() {
return InnerExecutorService.INSTANCE;
}
public ConcurrentHashMap getFutureMap() {
return futureMap;
}
public void execute(Runnable runnable) {
if (runnable != null) {
executorService.execute(runnable);
}
}
/**
* @param runnable
* @param delay 延迟时间
* @param timeUnit 时间单位
*/
public void sheduler(Runnable runnable, long delay, TimeUnit timeUnit) {
if (runnable != null) {
executorService.schedule(runnable, delay, timeUnit);
}
}
/**
* 执行延时周期性任务
*
* @param runnable {@code LgExecutorSercice.JobRunnable}
* @param initialDelay 延迟时间
* @param period 周期时间
* @param timeUnit 时间单位
*/
public void sheduler(T runnable, long initialDelay, long period, TimeUnit timeUnit) {
if (runnable != null) {
Future future = executorService.scheduleAtFixedRate(runnable, initialDelay, period, timeUnit);
futureMap.put(runnable.getJobId(), future);
}
}
public static abstract class JobRunnable implements Runnable {
private String jobId;
public JobRunnable(@NonNull String jobId) {
this.jobId = jobId;
}
/**
* 强制终止定时线程
*/
public void terminal() {
try {
Future future = LgExecutorService.getInstance().getFutureMap().remove(jobId);
future.cancel(true);
} finally {
System.out.println("jobId " + jobId + " had cancel");
}
}
public String getJobId() {
return jobId;
}
}
}