Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
线程池如何实现线程的复用: 通过BlockingQueue阻塞队列,存放Runnable对象,检查线程队列中的线程,存在线程空闲且未被销毁,继续使用此线程执行任务。
使用线程池的优点:
- 1、重用已经创建好的线程,避免频繁创建和销毁的系统开销
- 2、控制线程并发数,合理使用系统资源,提高应用的性能
- 3、手动管理线程,比如定时执行、取消执行等
1、Executors的使用
public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.execute(new Task());
}
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
复制代码
2、ThreadPoolExecutor的介绍
ThreadPoolExecutor构造函数:
-
corePoolSize 核心线程数,当提交一个任务(Runnable对象),只要当前线程数小于corePoolSize,就会创建一个线程。默认情况下,核心线程即使处于空闲状态,也会保持存活状态。如果将ThreadPoolExecutor的allowCoreThreadTimeout设置为true,则核心线程也会存在超时策略,当等待时间超过keepAliveTime,核心线程就会结束。
-
blockingQueue 维护一个runnable对象的阻塞队列,当队列为空的时候,此时取出任务的操作会被阻塞;在满队列的时候,添加操作同样被阻塞。不同的blockingQueue对线程池运行逻辑有很大影响,可以选择以下几个阻塞队列:
ArrayBlockingQueue:基于数组结构的有界阻塞队列。 LinkedBlockingQueue:一个基于链表结构的无界阻塞队列。 SynchronousQueue:一个不存储元素的阻塞队列。 PriorityBlockingQueue:一个具有优先级的无界阻塞队列。 复制代码
-
keepAliveTime 线程执行结束后,保持存活的时间。
-
TimeUnit: keepAliveTime的时间单位,可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。TimeUnit工具类
-
ThreadFactory: 用于设置创建线程的工厂,通过线程工厂给线程设置名字。
-
RejectedExecutionHandler: 线程池队列饱和之后的执行策略,默认是采用AbortPolicy。JDK提供四种实现方式:
AbortPolicy:直接抛出异常 CallerRunsPolicy :只用调用者所在线程来运行任务 DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务 DiscardPolicy : 不处理,丢弃掉 复制代码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue 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;
}
复制代码
3、Executors
Excutors是个工厂类,里面提供多了创建多种不同线程池的方法,常用的有 :
3.1 newFixedThreadPool
创建固定数目的核心线程数,LinkedBlockingQueue为无界的阻塞队列,即使线程都处于运行状态,还可以不断的往blockingQueue中插入runnable对象。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
复制代码
3.2 newSingleThreadExecutor
有且只有一个核心线程,LinkedBlockingQueue为无界的阻塞队列,即使线程都处于运行状态,还可以不断的往blockingQueue中插入runnable对象。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
复制代码
3.3 newSingleThreadExecutor
无核心线程,可以创建N个非核心线程;在线程任务执行完,60秒后,如果没有新的任务到达,则线程消亡;SynchronousQueue插入操作必须等上一个元素被移除之后,否则插入操作一直处于阻塞状态。
如果线程仍然存活且入队成功,则复用线程进行执行;否则创建新的线程;如果都失败,reject(runnable)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
复制代码
3.4 newScheduledThreadPool
定时任务,可以指定initialDelay或者周期性定时任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
复制代码
4、ThreadPoolExecutor
ThreadPoolExecutor # execute(runnable)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
复制代码
-
1、如果目前运行线程数少于核心线程数,addWorker(command,true)
-
2、如果线程还在运行,且command入队成功,进行double-check,以免在上次检查之后线程已挂或者线程池已挂。如果已挂,则reject(command);如果线程池中线程数目为0,则还没有创建过线程,addWorker(null, false)
-
3、如果以上两步均不满足,addWorker(command, false) 尝试新建非核心线程。创建失败,则reject(command)
细节分析
- addWorker(command,true) 创建一个带command的核心线程
- addWorker(null, false) 创建一个command为空的非核心线程,尔后会通过getTask()从workQueue中取出一个runnable对象执行
- addWorker(command, false) 创建一个带command的非核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); //实例化worker对象
final Thread t = w.thread; //获得worker对象中的thread
if (t != null) {
if (workerAdded) {
t.start();
}
}
}
return workerStarted;
}
复制代码
Worker
addWorker()中会new Worker(command),实例化worker对象成功,执行work.thread.start()
thread.start() -> runnable.run() -> runWorker(worker)
最终执行传入的这个command对象
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread;
Runnable firstTask; //线程执行的第一个task,可能为null
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* firstTask赋值,并通过threadFactory创建线程,将当前worker对象作为runnable对象用作thread初始化
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** 执行thread.start() -> runnable.run() -> runWorker() */
public void run() {
runWorker(this);
}
}
复制代码
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; //task可能为null
w.firstTask = null;
try {
while (task != null || (task = getTask()) != null) {
task.run(); //最终执行command
task = null; //清除task
}
} finally {
processWorkerExit(w, completedAbruptly);
}
}
复制代码
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { //如果线程存活且workQueue是个空队列,返回null
return null;
}
//如果线程已经超时,仍没有任务到达,返回null
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
}
}
复制代码
细节:
1、在task为null的状态,就得通过getTask不断的从workQueue中取出task
1、execute(command)中,addWorker(null, false)
2、执行完task.run(),task = null,清除状态
复制代码
2、getTask()返回runnable对象
timed为true,受keepAliveTime限制,有超时机制;否则无超时机制,无限等待
也就是这里设置了核心线程会一直存活,非核心线程在超时后会消亡
1、如果是核心线程且allowCoreThreadTimeOut为false, workQueue.take(),一直等待直到任务available
2、如果是非核心线程,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 带超时机制,如果超时还没有任务到达,则返回null,
复制代码
参考
java并发编程--Executor框架
Java线程池实现原理与源码解析(jdk1.8)
Java 线程池ThreadPoolExecutor(基于jdk1.8)