一)ThreadPoolExecutor简介
ThreadPoolExecutor是线程池具体实现类,继承了AbstractExecutorService抽象类。
作用:多线程的频繁运行和释放,消耗的时间会比较久。线程池主要是用来存放一定数量线程的线程集合,当线程使用时,从线程池中分配一个空闲的线程来执行对应的程序,当超过线程池的固定容量时,会有一部分线程会处于等待状态。线程池通过相应的调度策略和拒绝策略,对添加的线程进行管理。
二)ThreadPoolExecutor数据结构
1)阻塞队列workQueue
private final BlockingQueue<Runnable> workQueue;
描述:BlockingQueue<E>是一个阻塞队列接口,常用的实现类有ArrayBlockingQueue<E>有界阻塞队列和LinkedBlockingQueue<E>无界阻塞队列。当线程池中的线程数超过它的容量的时候,线程会进入阻塞队列进行阻塞等待。
2)互斥锁mainLock
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition(); // 线程终止条件
描述:主要是用来保证线程操作安全,实现了对线程池的互斥访问。
3)线程池运行任务集合workers
private final HashSet<Worker> workers = new HashSet<Worker>();
描述:Worker对应了一个线程,workers包含了一个线程集合。当Worker对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。通过该方式实现了"允许多个线程同时运行"。
4)其它线程池参数
private int largestPoolSize; // 线程池中线程数量曾经达到过的最大值。
private long completedTaskCount; // 已完成任务数量。
private volatile ThreadFactory threadFactory; // ThreadFactory对象, 用于创建并运行线程。
private volatile RejectedExecutionHandler handler; // 线程池拒绝策略处理方式。内部实现。
private volatile long keepAliveTime; // 保持线程池中线程存活时间。当线程池处于空闲状态的时候,超过keepAliveTime时间的空闲线程都会被终止。
private volatile boolean allowCoreThreadTimeOut; // 是否允许"线程在空闲状态时,仍然能够存活"。
private volatile int corePoolSize; // 线程池核心数量,也是“线程池中实际运行的线程数量”
private volatile int maximumPoolSize; // 线程池最大数量
三)线程池几种状态
线程和进程的五个阶段:新建、就绪、运行、阻塞、死亡。
新建状态:使用new关键字或Thread及其子类创建一个线程对象后,线程为新增状态。
就绪状态:当线程调用start()方法时,该线程进入就绪状态。线程处于就绪的队列中,等待JVM线程调度器调度。
运行状态:当JVM调度器调度线程时,会执行run()方法,线程处于运行状态中。运行状态的线程可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:当线程调用一些特殊方法时,如sleep(睡眠)、wait(等待)。线程失去了占用资源之后,该线程变为阻塞状态。
1)等待阻塞:运行中的线程调用wait()方法,使线程进入到等待阻塞状态。
2)同步阻塞:当线程获取synchronized同步锁失败(同步锁被其它线程占用)。
3)其他阻塞:线程调用 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
线程池的5种状态:Running、SHUTDOWN、STOP、TIDYING、TERMINATED。
RUNNING:线程池在RUNNING状态时,能够接收新任务,并对已添加的任务进行处理。
SHUTDOWN:线程池在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
STOP:线程池在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理中的任务。
TIDYING:当所有的任务已终止时,线程池会变为TIDYING状态。
TERMINATED:调用shutdown()方法,线程池会终止运行,就会变成TERMINATED状态。
四)ThreadPoolExecutor线程池创建
第一种:创建一个新的ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝执行处理程序。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
corePoolSize:即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 该队列将仅保存execute方法提交的Runnable任务。
第二种:创建一个新的ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
corePoolSize:要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 这个队列只会保存execute方法提交的Runnable任务。
threadFactory:执行程序创建新线程时使用的工厂。
第三种:创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
corePoolSize:要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut。
maximumPoolSize:池中允许的最大线程数。
keepAliveTime:当线程数大于核心时,这是多余的空闲线程在终止之前等待新任务的最大时间。
unit:keepAliveTime参数的时间单位。
workQueue:在执行任务之前用于保存任务的队列。 这个队列只会保存execute方法提交的Runnable任务。
threadFactory:执行程序创建新线程时使用的工厂。
handler:执行被阻止时使用的处理程序,因为达到线程限制和队列容量。
第四种:创建一个新 ThreadPoolExecutor给定的初始参数。最终都是调用该方法。
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize; // 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut。
this.maximumPoolSize = maximumPoolSize; // 池中允许的最大线程数。
this.workQueue = workQueue; // 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。
this.keepAliveTime = unit.toNanos(keepAliveTime); // keepAliveTime参数的时间单位,当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。
this.threadFactory = threadFactory; // 执行程序创建新线程时使用的工厂。
this.handler = handler; // 执行被阻止时使用的处理程序,因为达到线程限制和队列容量。
}
五)ThreadPoolExecutor拒绝策略
该类提供了四种线程池拒绝策略。
1)ThreadPoolExecutor.AbortPolicy:当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException异常。
2)ThreadPoolExecutor.CallerRunsPolicy:当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
3)ThreadPoolExecutor.DiscardOldestPolicy:当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
4)ThreadPoolExecutor.DiscardPolicy:当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
六)execute()、addWorker()
1)void execute(Runnable command)
将任务添加到线程池中,并在将来某个时候执行给定的任务。
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) // 当线程池中没有运行的任务时,添加一个null任务
addWorker(null, false);
}
else if (!addWorker(command, false)) // 新创建一个线程,并启动,如启动失败,根据线程池拒绝策略返回信息
reject(command);
}
2)boolean addWorker(Runnable firstTask, boolean core)
添加线程任务,并启动执行给定的任务
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) { // 一直运行线程池,除非调用了shutdown()关闭线程池方法
int c = ctl.get(); // 获取线程池中任务数量
int rs = runStateOf(c); // 获取线程池运行状态
// Check if queue empty only if necessary. // 数据有效性检查
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c); // 获取运行的线程数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize)) // 当线程数量超过时,返回false
return false;
if (compareAndIncrementWorkerCount(c)) // 重新设置线程数量
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs) // 检查线程池的状态,当线程状态不一致时,一直循环判断
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get()); // 获取线程运行状态
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); // 将线程添加线程集合中
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) { // 当线程检查OK时,启动该线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted) // 当线程因某种原先失败时,把任务删除,并从线程集合中移除,并终止线程
addWorkerFailed(w);
}
return workerStarted;
}
七)ThreadPoolExecutor实现
第一步:创建了一个类,继承Runnable接口,实现run方法。
第二步:创建一个ThreadPoolExecutor线程池,指定核心线程池大小,最大线程池大小,一些默认参数。
第三步:循环创建多个线程实例,并把线程一个个添加到线程池中,通过线程池来启动运行。
源码:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
// 自定义类
static class MyRunnable implements Runnable {
private String threadName;
public MyRunnable(String threadName) {
this.threadName = threadName;
}
@Override
public void run() {
try {
System.out.println("当前线程名" + Thread.currentThread().getName() + ", 任务" + threadName + " is running!");
Thread.sleep(100); // 睡眠100毫秒,方便测试
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 创建一个ThreadPoolExecutor线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
MyRunnable my = null;
for (int i = 0; i < 5; i++) {
my = new MyRunnable("MyRunnable" + (i+1));
pool.execute(my); // 将线程添加到线程中
}
// 关闭线程池
pool.shutdown();
}
}
打印效果图:
识别二维码关注个人微信公众号
本章完结,待续,欢迎转载!
本文说明:该文章属于原创,如需转载,请标明文章转载来源!