1. 线程池
1.1 ThreadPoolExecutor
1.1.1 构造方法
在ThreadPoolExecutor类中提供了四个构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造器中各个参数的含义:
-
corePoolSize:核心线程的大小。默认情况下,在创建了线程池后,线程池中并没有任何线程,而是等待有任务到来后才创建线程去执行任务。除非调用了 prestartAllCoreThreads()、prestartCoreThread() 方法----在没有任务到来之前,就创建 corePoolSize 个线程或一个线程。
-
maximumPoolSize:线程池中最多能创建的线程数
-
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0
-
unit:参数keepAliveTime的时间单位
-
workQueue:阻塞队列。它用来存储等待执行的任务
阻塞队列:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue
FIFO:ArrayBlockingQueue、LinkedBlockingQueue
优先级:PriorityBlockingQueue
放和取交替:SynchronousQueueArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous
-
threadFactory:线程工厂,主要用来创建线程
-
handler:表示当拒绝处理任务时的策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
1.1.2 方法
在 ThreadPoolExecutor 类中的几个非常重要的方法:
- execute():实际上是Executor中声明的方法。这个方法是 ThreadPoolExecutor 的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行
- submit():实际上是在ExecutorService中声明的方法,在 AbstractExecutorService 就已经有了具体的实现。这个方法也是用来向线程池提交任务的,但是它和 execute() 方法不同,它能够返回任务执行的结果,去看 submit() 方法的实现,会发现它实际上还是调用的 execute() 方法,只不过它利用了 Future 来获取任务执行结果
- shutdown() 和 shutdownNow() 是用来关闭线程池的
1.1.3 成员变量
ThreadPoolExecutor 中的重要成员变量
AtomicInteger ctl
AtomicInteger 类型的 ctl 代表了 ThreadPoolExecutor 中的控制状态,它是一个复合类型的成员变量(原子整数),借助高低位包装了两个概念:
- workerCount:线程池中当前活动的线程数量,占据ctl的低29位(COUNT_BITS = Integer.SIZE - 3 就代表了workerCount所占位数)
- runState:线程池运行状态,占据ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态
workerCount:代表了线程池中当前活动的线程数量。它的下限阈值:0;它的上限阈值:CAPACITY = (1 << COUNT_BITS) - 1
1 左移 29 位:00000000 00000000 00000000 00000001 => 0010000 00000000 00000000 00000000
再减去 1:00011111 11111111 11111111 11111111
前三位代表线程池运行状态 runState,所以这里 workerCount 的理论最大值就应该是 29 个 1,即 536870911
从 AtomicInteger ctl 中解析出 workerCount 的方法:
private static int workerCountOf(int c) { return c & CAPACITY; }
计算逻辑:参数 c 代表 ctl 的值,将其与 CAPACITY 进行与操作 &(与 00011111 11111111 11111111 11111111 进行与操作)。c 的前三位通过与 000 进行与操作,无论 c 前三位为何值,最终都会变成 000,也就是舍弃前三位的值;而 c 的低 29 位与 29 个 1 进行与操作,c 的低 29 位还是会保持原值,这样就从AtomicInteger ctl 中解析出了 workerCount 的值。
runState:线程池运行状态。五种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED
- RUNNING:接受新任务,并处理队列任务
private static final int RUNNING = -1 << COUNT_BITS;
-1 在 Java 底层是由 32 个 1 表示的,左移 29 位的话,即11100000 00000000 00000000 00000000,也就是低 29 位全部为 0,高 3 位全部为 1 的话,表示RUNNING 状态,即 -536870912
-
SHUTDOWN:不接受新任务,但会处理队列任务
-
STOP:不接受新任务,不会处理队列任务,而且会中断正在处理过程中的任务
-
TIDYING:所有的任务已结束,workerCount 为 0,线程过渡到 TIDYING 状态,将会执行 terminated() 钩子方法
-
TERMINATED:terminated() 方法已经完成
线程池状态转换:
- RUNNING => SHUTDOWN:调用了 shutdown() 方法;线程池实现了 finalize() 方法,在里面调用了shutdown() 方法
- (RUNNING or SHUTDOWN) => STOP:调用shutdownNow()方法
- SHUTDOWN => TIDYING:线程池和队列均为空
- STOP => TIDYING:线程池为空
- TIDYING => TERMINATED:terminated() 钩子方法完成
线程池的关闭
ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow()
- shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
- shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
BlockingQueue workQueue
workQueue 是用于持有任务并将其转换成工作线程 worker 的队列
volatile int corePoolSize
核心线程池大小,保持存活的工作线程的最小数目,当小于 corePoolSize 时,会直接启动新的一个线程来处理任务,而不管线程池中是否有空闲线程
1.1.4 线程池工作原理
查看 ThreadPoolExecutor.execute() 方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 判断当前活跃线程数是否小于 corePoolSize
if (workerCountOf(c) < corePoolSize) {
// 如果小于,则调用 addWork() 方法创建线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 如果大于等于 corePoolSize,则将任务添加到 workQueue 队列
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);
}
}
// 如果放入 workQueue 队列失败,则创建非核心线程执行任务
else if (!addWorker(command, false)) {
// 如果这时创建线程失败(当前线程数大于等于 maximumPoolSize),则调用 reject() 方法
reject(command);
}
}
大概就是以上逻辑吧~~
1.1.5 线程池案例
public class MyTask implements Runnable {
private int taskNum;
public MyTask(int taskNum) {
this.taskNum = taskNum;
}
@Override
public void run() {
System.out.println("正在执行 task " + taskNum);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("task " + taskNum + "执行完毕");
}
}
}
public class Test {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5));
for (int i = 0; i < 15 ; i++) {
MyTask myTask = new MyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +
executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
executor.shutdown();
}
}
从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。
不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
Executors.newCachedThreadPool():创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor():创建容量为1的缓冲池
Executors.newFixedThreadPool(int):创建固定容量大小的缓冲池
下面是这三个静态方法的具体实现:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。