回顾 java.util.concurrent.Executors 提供的几种线程池,查看源码发现,除了jdk1.8 新增的newWorkStealingPool,其余的都是使用 ThreadPoolExecutor。所以,我们先来看看 ThreadPoolExecutor的神秘力量
ThreadPoolExecutor
- 了解一个类,先看其构造函数,ThreadPoolExecutor 有以下四个构造函数
/* 构造函数1: 5个参数,调用 7 个参数的构造函数 */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/* 构造函数2: 6个参数,调用 7 个参数的构造函数,比构造函数1多了ThreadFactory threadFactory */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
/* 构造函数3:6个参数,调用 7 个参数的构造函数,比构造函数1 多了RejectedExecutionHandler handler */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
/* 构造函数4: 7个参数,真正干活的构造 */
/* 各个参数的作用
* param1:int corePoolSize ==> 线程池中核心的线程数,即一直工作的个数
* param2:int maximumPoolSize ==> 线程池中最大的线程数,即最忙碌时能开启的线程个数
* param3:long keepAliveTime ==> 空闲线程的存活时间,即不用的线程多久关闭
* param4:TimeUnit unit ==> 与 param3 配合使用,制定时间单位
* param5:BlockingQueue<Runnable> workQueue ==> 存储任务的阻塞队列,当任务数 > 核心线程corePoolSize 时存储,注意,这里不会先开启线程,任务队列满了才会开启线程,后面证明
* param6:ThreadFactory threadFactory ==> 线程工厂,负责创建线程工作
* param7:RejectedExecutionHandler handler ==> 拒绝执行策略,即线程池满了,新任该怎么处理,defaultHandler = new AbortPolicy();
*/
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;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
证明 param5 所说,超过核心线程数,先放进任务队列,任务队列满了,直接开启线程执行新添加的任务
- 这里是结果解释,代码解释,在 java 线程池的使用及原理(四):线程池的运作原理 ,execute源码处可知。
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class BlockingTest {
public static void main(String[] args) {
/* 核心线程:2 最大线程:4 等待队列:2 */
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(2));
for(int i = 1; i <= 4; i++) {
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(3000);
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是先添加的任务" + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
for(int i = 1; i <= 2; i++) {
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(3000);
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是后添加的任务 -->" + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
threadPool.shutdown();
}
}
- 可以看到,后添加的任务开启线程执行,先于 任务3、4。
ThreadPoolExecutor 四种拒绝策略
- AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
- DiscardPolicy:丢弃任务,但是不抛出异常。
- DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
- CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
1. AbortPolicy(默认)
- AbortPolicy 类源码,可以看到,拒绝策略是 throw RejectedExecutionException()
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class AbortPolicyTest {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 5,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(2));
for(int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName());
});
}
Thread.sleep(1000);
System.out.println("====================== 我是华丽的分割线 ====================");
for(int i = 1; i <= 5; i++) {
threadPool.execute(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName());
});
}
}
}
- 抛出 RejectedExecutionException 错误,线程不停止,但不也继续往下执行?
- 因为从报错信息也可以看出,错误是main线程抛出的, 又 ThreadPoolExecutor 的 allowCoreThreadTimeOut 默认为false,即核心线程一直保持运行,所以线程一直阻塞。
- 因为从报错信息也可以看出,错误是main线程抛出的, 又 ThreadPoolExecutor 的 allowCoreThreadTimeOut 默认为false,即核心线程一直保持运行,所以线程一直阻塞。
2. DiscardPolicy
- DiscardPolicy 类源码,rejectedExecution 为空,不做任何工作
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
- 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
import java.util.concurrent.TimeUnit;
public class DiscardPolicyTest {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 5,
0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2),
new DiscardPolicy());
for(int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName());
});
}
// 确保之前的线程不受影响
Thread.sleep(3000);
System.out.println("====================== 我是华丽的分割线 ======================");
for(int i = 1; i <= 10; i++) {
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是 rejectedExecution后执行的线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
});
}
threadPool.shutdown();
}
}
3. DiscardOldestPolicy
- DiscardOldestPolicy 类源码,很明显可以看到,poll() 一个,在执行 execute()
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.TimeUnit;
public class DiscardOldestPolicyTest {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5,
0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2),
new DiscardOldestPolicy());
for(int i = 1; i <= 20; i++) {
int finalI = i;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.execute(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
});
}
Thread.sleep(10000);
System.out.println("====================== 我是华丽的分割线 ====================");
for(int i = 1; i <= 20; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
});
}
threadPool.shutdown();
}
}
- 可以看到,i = 19、20 输出了, 可以证明,最后的任务会挤兑前面的任务
4. CallerRunsPolicy
- CallerRunsPolicy 类源码,如果有阻塞,调用者直接执行任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
- 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit;
public class CallerRunsPolicyTest {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5,
0L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(2),
new CallerRunsPolicy());
for(int i = 1; i <= 10; i++) {
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
});
}
Thread.sleep(3000);
System.out.println("====================== 我是华丽的分割线 ====================");
for(int i = 1; i <= 10; i++) {
int finalI = i;
threadPool.execute(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
});
}
threadPool.shutdown();
}
}
回看 java 提供的线程池
newSingleThreadExecutor 、newFixedThreadPool
- 两者会提供一个无界的等待队列,当等待队列过多时,会报 OOM 异常
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>());
}
- 代码示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class newSingleThreadExecutorTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
while(true) {
executorService.execute(() -> {
byte[] bytes = new byte[1024 * 1024 * 30];
System.out.println("===============" + Thread.currentThread().getName());
});
}
executorService.shutdown();
}
}
- 为了凸显内存异常,我设置了较小的堆内存参数 -Xms60m -Xmx60m
newCachedThreadPool、newScheduledThreadPool
- 两者会无限创建线程,报 OOM 异常
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- 为了凸显内存异常,我设置了较小的堆内存参数 -Xms60m -Xmx60m
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewCachedThreadPoolTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
while(true) {
executorService.execute(() -> {
byte[] bytes = new byte[1024 * 1024];
try {
Thread.sleep(10000);
System.out.println("===============" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
}
- 除此之外,还可自定义等待队列,线程工厂,线程存活之间等。