泊头做网站的,wordpress 外观,seo营销,科技背景图自定义线程池 4.0
1. 简介
上次我们实现了自定义线程池的 3.1 版本#xff0c;提供了线程工厂创建线程和工具类创建简单线程池的功能#xff0c;增强了线程池的灵活性#xff0c;并且用起来更加方便了#xff0c;本文我们将做如下的优化#xff1a;
给线程池添加关闭的…自定义线程池 4.0
1. 简介
上次我们实现了自定义线程池的 3.1 版本提供了线程工厂创建线程和工具类创建简单线程池的功能增强了线程池的灵活性并且用起来更加方便了本文我们将做如下的优化
给线程池添加关闭的方法。
这个功能看起来容易实际做起来却很难。
2. 总体设计
首先我们需要思考什么叫关闭线程池是执行完堆积的任务再让线程停止还是让线程立即停止实际上这两种关闭线程池都可能用的到。先给这两个关闭的方法起名字吧然后再说明使用场景
shutdownWhenNoTask()执行完堆积的任务再让线程停止。使用的场景在于任务比较重要必须得执行但自己不想执行想委托线程池执行。shutdownNow()让线程立即停止返回任务队列中的所有任务。使用的场景在于任务不重要想要立即停止线程池或者自己来执行没有执行的任务不依赖线程池执行。
其次当线程池关闭后是无法再提交任务的所以在线程池关闭后再调用 submit() 方法直接拒绝。
接着如何知道线程池处于什么状态呢这有些困难我先讲讲我的设计给线程池添加一个状态变量 state然后定义 2 个常量——RUNNING, SHUTDOWN分别表示 运行状态 和 关闭状态state 初始化为运行状态在调用关闭方法时切换成关闭状态。但是关闭方法可能会被多个线程调用所以需要 将 state 的类型设计成原子类这样这个状态变量就是线程安全的。
最后如何让线程停止呢这个问题思考起来就很复杂了让我们进入标题 3 吧
3. “让线程停止”的设计方案
3.1 Thread.interrupt()
Thread 有一个成员方法 interrupt()这个方法并不是让线程直接停止而是给线程打上停止的 标记之后可以通过调用另一个成员方法 isInterrupted() 查看线程是否被打上停止标记。此外调用这个方法会唤醒阻塞的线程然后抛出 InterruptedException这就是很多阻塞方法都需要处理 InterruptedException 异常的原因。
如果使用这个方法来向所有线程传递中断信号那么可能会出现如下的情况
线程 A 在执行任务时阻塞线程 B 调用方法停止线程池从而调用线程 A 的 interrupt() 方法给线程 A 打上停止标记并唤醒线程 A。目前看来还正常但如果线程 A 做了如下的异常处理通过 Thread.interrupted() 清理了中断标记那这个中断标记就无法被我们识别从而无法使用中断标记使线程退出
try {...
} catch (InterruptedException e) {Thread.interrupted();...
}3.2 POISON_PILL
POISON_PILL(毒丸) 是一种常见的设计模式用于 安全地终止并发系统或进程间通信通常通过 通过发送一个特殊的“毒丸”消息来通知接收者停止处理从而优雅地关闭系统 的方式实现它的核心思想如下
信号机制发送一个特殊的消息毒丸作为终止信号。安全终止接收者收到毒丸后完成当前任务后停止处理避免数据丢失或资源泄漏。解耦控制发送者和接收者无需直接通信通过消息传递实现异步终止。
我们完全可以使用毒丸来终止线程池中的所有线程当调用停止方法时向任务队列中投放一个毒丸只要线程拿到毒丸就直接退出吗并不行如果这时一个线程直接退出了那其他线程怎么办难道要在停止方法中投放与当前线程数量相同数量的毒丸吗这样也不太好我们可以把线程的退出想象成一个“流”在这个“流”上前面的事情处理完之后需要通知后面的事情开始处理同时也可以在没有后续事情时选择不通知也就是说我们可以在线程退出前向任务队列中投放一个毒丸这样线程就前赴后继地拿到毒丸、退出了。
于是我们可以写出一个投放毒丸的方法这个方法下面需要优化目前只是一个简单的实现
public void offerPoisonPill() {synchronized (threadPoolMonitor) {taskQueue.offer(POISON_PILL); // POISON_PILL 是一个 Runnable 类型的常量}
}4. 两个关闭方法的实现
4.1 shutdownWhenNoTask()
这个方法要做的事情很简单只需要将线程池的状态切换成关闭状态然后再向队列中投放一个毒丸即可。
4.2 shutdownNow()
这个方法要做的事情稍微有点复杂除了切换线程池状态和投放毒丸之外还需要做两件事
给所有线程发送中断信号尝试让它们中断。将队列中的所有任务放到一个集合中并返回。
5. 投放毒丸方法的优化
标题 3.2 中实现的投放毒丸是有问题的假设任务队列已满则会投放失败所以我们需要得到 offer() 方法返回 true 的结果从而保证毒丸真正放到队列中。
5.1 v1 多次投放
有一种十分容易想到的做法
private void doOfferPoisonPill() {// 不断投放毒丸直到成功为止while (!taskQueue.offer(POISON_PILL)) {}
}但是这种做法很耗性能只要投放不成功就一直重试连休息的时间都没有如果让线程休眠一段时间会稍微好一点但也属于线程在 忙等待。
5.2 v2 阻塞投放
于是想到了更高级的做法只要投放不成功就等待队列有空余位置于是诞生了如下的代码
/*** 任务队列的锁用于生成一个 {link Condition} 对象*/
private final Lock taskQueueLock new ReentrantLock();/*** 任务队列已满的条件对象*/
private final Condition taskQueueNotFull taskQueueLock.newCondition();private void doOfferPoisonPill() {// 不断投放毒丸直到成功为止while (!taskQueue.offer(POISON_PILL)) {taskQueueLock.lock();try {taskQueueNotFull.await();} catch (InterruptedException ignore) {} finally {taskQueueLock.unlock();}}
}更重要的是我们不能再让其他类随便调用任务队列取出元素的方法了因为我们需要 在取出元素时唤醒在这里阻塞等待队列空余位置的线程所以我们需要将其取出元素的方法包装起来如下所示
/*** 使用 {link BlockingQueue#take()} 方法从队列中取出任务** return 任务队列中的任务*/
private Runnable takeTaskFromQueue() throws InterruptedException {taskQueueLock.lock();try {Runnable task taskQueue.take();taskQueueNotFull.signalAll();return task;} finally {taskQueueLock.unlock();}
}/*** 使用 {link BlockingQueue#take()} 方法从队列中取出任务** param nanos 等待的时间单位ns* return 任务队列中的任务如果等待超时则返回 {code null}*/
private Runnable pollTaskFromQueue(long nanos) throws InterruptedException {taskQueueLock.lock();try {Runnable task taskQueue.poll(nanos, TimeUnit.NANOSECONDS);if (task ! null) {taskQueueNotFull.signalAll();}return task;} finally {taskQueueLock.unlock();}
}6. 实现 4.0 版本
听明白上面这些设计后我们终于能实现 4.0 版本了
6.1 Worker
public abstract class Worker implements Runnable {/*** 线程执行的初始任务*/private Runnable initialTask;/*** 对 strong真正运行的线程/strong 的引用用于调用其 {link Thread#start()} 方法启动线程*/private final Thread actuallyRunningThread;/*** {link Worker} 存在的线程池*/protected final ThreadPool4_0 threadPool;public Worker(Runnable initialTask, SetWorker workerPool, ThreadFactory threadFactory, ThreadPool4_0 threadPool) {this.initialTask initialTask;this.actuallyRunningThread threadFactory.newThread(this);workerPool.add(this);this.threadPool threadPool;}Overridepublic final void run() {initialTask.run();initialTask null; // help GCtry {while (true) {Runnable t getTask();if (t null) {// 检查是否获取到任务了如果没有则退出循环停止运行break;} else if (t ThreadPool4_0.POISON_PILL) {// 如果任务是毒丸则先往队列中再放一个毒丸然后退出循环threadPool.offerPoisonPillIfThreadRest();// 以下这句话只是为了测试在正式环境中最好注释掉LogUtil.infoWithTimeAndThreadName(发现毒丸退出循环);break;}t.run();}} finally {onWorkerExit();}}/*** 启动内部保存的线程*/public final void start() {actuallyRunningThread.start();}/*** 给正在运行中的线程打上中断标记*/public final void interrupt() {actuallyRunningThread.interrupt();}/*** 获取任务当返回 {code null} 时这个 {link Worker} 对象就退出循环* p* 使用模板方法模式交给子类实现** return 获取到的任务*/protected abstract Runnable getTask();/*** 当 {link Worker} 准备退出时执行的回调函数* p* 用于将 {link Worker} 对象从线程池中移除*/private void onWorkerExit() {threadPool.removeWorkerFromThreadPool(this);}
}6.2 ThreadPool4_0
public class ThreadPool4_0 {/*** 线程池中核心线程的最大数量*/private final int corePoolSize;/*** 线程池中线程的最大数量*/private final int maxPoolSize;/*** 临时线程阻塞的最长时间单位ns超过这个时间还没有领取到任务就直接退出*/private final long keepAliveTime;/*** 任务队列*/private final BlockingQueueRunnable taskQueue;/*** 拒绝策略用于在无法执行任务的时候拒绝任务*/private final RejectPolicy rejectPolicy;/*** 线程工厂*/private final ThreadFactory threadFactory;/*** 默认的线程工厂*/private static final ThreadFactory DEFAULT_THREAD_FACTORY new ThreadFactory() {/*** 计数器用来记录当前创建的是第几个线程从 0 开始*/private int counter 0;Overridepublic Thread newThread(Runnable r) {return new Thread(r, thread-pool- counter);}};/*** 构造一个线程池默认参数如下* ul* li拒绝策略默认为抛出异常的拒绝策略/li* li线程工厂默认为线程添加了简单的名字 thread-pool-?/li* /ul** param corePoolSize 线程池中核心线程的最大数量* param maxPoolSize 线程池中线程的最大数量* param keepAliveTime 临时线程阻塞的最长时间* param unit 时间的单位* param taskQueue 任务队列*/public ThreadPool4_0(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueueRunnable taskQueue) {this(corePoolSize, maxPoolSize, keepAliveTime, unit, taskQueue, RejectPolicy.THROW_EXCEPTION);}/*** 构造一个线程池默认参数如下* ul* li拒绝策略默认为抛出异常的拒绝策略/li* /ul** param corePoolSize 线程池中核心线程的最大数量* param maxPoolSize 线程池中线程的最大数量* param keepAliveTime 临时线程阻塞的最长时间* param unit 时间的单位* param taskQueue 任务队列* param threadFactory 线程工厂*/public ThreadPool4_0(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueueRunnable taskQueue, ThreadFactory threadFactory) {this(corePoolSize, maxPoolSize, keepAliveTime, unit, taskQueue, RejectPolicy.THROW_EXCEPTION, threadFactory);}/*** 构造一个线程池默认参数如下* ul* li线程工厂默认为线程添加了简单的名字 thread-pool-?/li* /ul** param corePoolSize 线程池中核心线程的最大数量* param maxPoolSize 线程池中线程的最大数量* param keepAliveTime 临时线程阻塞的最长时间* param unit 时间的单位* param taskQueue 任务队列* param rejectPolicy 拒绝策略*/public ThreadPool4_0(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueueRunnable taskQueue, RejectPolicy rejectPolicy) {this(corePoolSize, maxPoolSize, keepAliveTime, unit, taskQueue, rejectPolicy, DEFAULT_THREAD_FACTORY);}/*** 构造一个线程池** param corePoolSize 线程池中核心线程的最大数量* param maxPoolSize 线程池中线程的最大数量* param keepAliveTime 临时线程阻塞的最长时间* param unit 时间的单位* param taskQueue 任务队列* param rejectPolicy 拒绝策略* param threadFactory 线程工厂*/public ThreadPool4_0(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueueRunnable taskQueue, RejectPolicy rejectPolicy, ThreadFactory threadFactory) {this.corePoolSize corePoolSize;this.maxPoolSize maxPoolSize;this.keepAliveTime unit.toNanos(keepAliveTime);this.taskQueue taskQueue;this.rejectPolicy rejectPolicy;this.threadFactory threadFactory;}/*** 存放线程的集合使用 {link Set} 是因为 {link Set#remove(Object)} 性能更高*/private final SetWorker threadPool new HashSet();/*** 线程池的管程* p* 用于保证 strong将线程放入线程池/strong、strong从线程池中移除线程/strong 的互斥性* 同时也在保证 {link #currPoolSize} 相关操作的互斥性*/private final Object threadPoolMonitor new Object();/*** 线程池中当前线程数量这个值 threadPool.size()* 在创建新线程时增加在放毒丸时减少threadPool.size() 减少的时机晚于 currPoolSize*/private int currPoolSize 0;/*** h3核心线程执行的任务/h3* {link #getTask()} 方法会一直阻塞直到有新任务*/public final class CoreWorker extends Worker {public CoreWorker(Runnable initialTask, SetWorker workerPool, ThreadFactory threadFactory,ThreadPool4_0 threadPool) {super(initialTask, workerPool, threadFactory, threadPool);}Overrideprotected Runnable getTask() {try {return takeTaskFromQueue();} catch (InterruptedException e) {throw new RuntimeException(e);}}}/*** h3临时线程执行的任务/h3* {link #getTask()} 方法会在阻塞一定时间后如果还没有任务则会返回 {code null}*/public final class TempWorker extends Worker {public TempWorker(Runnable initialTask, SetWorker workerPool, ThreadFactory threadFactory,ThreadPool4_0 threadPool) {super(initialTask, workerPool, threadFactory, threadPool);}Overrideprotected Runnable getTask() {try {return pollTaskFromQueue(keepAliveTime);} catch (InterruptedException e) {throw new RuntimeException(e);}}}/*** 线程池的状态状态共有 2 种* ul* li{link #RUNNING} 运行状态/li* li{link #SHUTDOWN} 关闭状态调用了线程池的关闭方法/li* /ul*/private final AtomicInteger state new AtomicInteger(RUNNING);private static final int RUNNING 1;private static final int SHUTDOWN 2;/*** 提交任务** param task 待执行的任务*/public void submit(Runnable task) {// 如果线程池的状态不是 RUNNING 状态则直接拒绝任务if (state.get() ! RUNNING) {rejectPolicy.reject(this, task);return;}// 如果 线程数量 小于 最大核心线程数量则新建一个 核心线程 执行任务然后直接返回synchronized (threadPoolMonitor) {if (currPoolSize corePoolSize) {CoreWorker coreWorker new CoreWorker(task, threadPool, threadFactory, this);coreWorker.start();currPoolSize;return;}}// 如果能够放到任务队列中则直接返回if (taskQueue.offer(task)) {return;}// 如果 线程数量 小于 最大线程数量则新建一个 临时线程 执行任务synchronized (threadPoolMonitor) {if (currPoolSize maxPoolSize) {TempWorker tempWorker new TempWorker(task, threadPool, threadFactory, this);tempWorker.start();currPoolSize;return;}}// 线程数量到达最大线程数量任务队列已满执行拒绝策略rejectPolicy.reject(this, task);}/*** 在没有任务执行时停止所有线程* 当此方法被调用线程池会停止接受提交任务然后等待线程将 它们正在执行的任务 和 任务队列中的任务 都执行完毕之后让所有线程退出*/public void shutdownWhenNoTask() {// 将状态从 RUNNING 切换到 SHUTDOWNif (state.compareAndSet(RUNNING, SHUTDOWN)) {// 如果切换成功则向任务队列中投放一个毒丸offerPoisonPill();}}/*** 立刻停止所有线程* 当此方法被调用线程池会停止接受提交任务给线程发送中断信号* p* 注意strong如果在调用此方法之前调用了 {link #shutdownWhenNoTask()} 方法不会立刻停止所有线程/strong** return 任务队列中的任务*/public ListRunnable shutdownNow() {// 将状态从 RUNNING 切换到 SHUTDOWN如果修改失败则表示线程池已经调用过关闭相关的方法了直接返回一个空集合即可if (!state.compareAndSet(RUNNING, SHUTDOWN)) {return new ArrayList();}// 给所有线程发送中断信号synchronized (threadPoolMonitor) {threadPool.forEach(Worker::interrupt);}// 将任务队列中的任务放到一个集合中ListRunnable taskList new ArrayList(taskQueue.size());taskQueue.drainTo(taskList);// 向任务队列中投放一个毒丸offerPoisonPill();// 返回任务集合return taskList;}/*** 获取当前线程池中的线程数量** return 当前线程池中的线程数量*/public int getCurrPoolSize() {synchronized (threadPoolMonitor) {return currPoolSize;}}/*** 获取当前任务队列中的任务数** return 当前任务队列中的任务数*/public int getCurrTaskNum() {return taskQueue.size();}/*** 丢弃任务队列 {link #taskQueue} 中的最旧的任务队头任务** return 任务队列中的最旧的任务队头任务*/public Runnable discardOldestTask() {return taskQueue.poll();}/*** 使用 {link BlockingQueue#take()} 方法从队列中取出任务** return 任务队列中的任务*/private Runnable takeTaskFromQueue() throws InterruptedException {taskQueueLock.lock();try {Runnable task taskQueue.take();taskQueueNotFull.signalAll();return task;} finally {taskQueueLock.unlock();}}/*** 使用 {link BlockingQueue#take()} 方法从队列中取出任务** param nanos 等待的时间单位ns* return 任务队列中的任务如果等待超时则返回 {code null}*/private Runnable pollTaskFromQueue(long nanos) throws InterruptedException {taskQueueLock.lock();try {Runnable task taskQueue.poll(nanos, TimeUnit.NANOSECONDS);if (task ! null) {taskQueueNotFull.signalAll();}return task;} finally {taskQueueLock.unlock();}}/*** 毒丸用于让线程池中的所有线程退出* 投放到任务队列中只要线程获取到这个任务就退出并且在 线程池中还有线程 的情况下将毒丸重新放回任务队列*/public static final Runnable POISON_PILL () - {};/*** 任务队列的锁用于生成一个 {link Condition} 对象*/private final Lock taskQueueLock new ReentrantLock();/*** 任务队列已满的条件对象*/private final Condition taskQueueNotFull taskQueueLock.newCondition();// 实际上投放毒丸的操作private void doOfferPoisonPill() {// 不断投放毒丸直到成功为止while (!taskQueue.offer(POISON_PILL)) {taskQueueLock.lock();try {taskQueueNotFull.await();} catch (InterruptedException ignore) {} finally {taskQueueLock.unlock();}}currPoolSize--;}/*** 向任务队列中投放一个毒丸等待线程领取后退出*/public void offerPoisonPill() {synchronized (threadPoolMonitor) {doOfferPoisonPill();}}/*** 在线程池中还有其他线程的情况下向任务队列中投放一个毒丸等待线程领取后退出用于*/public void offerPoisonPillIfThreadRest() {synchronized (threadPoolMonitor) {if (currPoolSize 0) {doOfferPoisonPill();}}}/*** 从 {link #threadPool} 中移除指定的 {link Worker} 对象** param worker 待移除的 {link Worker} 对象*/public void removeWorkerFromThreadPool(Worker worker) {synchronized (threadPoolMonitor) {threadPool.remove(worker);}}
}7. 测试程序
public class ThreadPool4_0Test {/*** 测试线程池 4.0 版本的基本功能*/Testpublic void test() throws InterruptedException {final int taskSize 3;CountDownLatch latch new CountDownLatch(taskSize);ThreadPool4_0 threadPool new ThreadPool4_0(1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(3));LogUtil.infoWithTimeAndThreadName(提交任务前);for (int i 0; i taskSize; i) {int finalI i;threadPool.submit(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}LogUtil.infoWithTimeAndThreadName(正在执行任务 finalI);latch.countDown();});}LogUtil.infoWithTimeAndThreadName(提交任务后);// 等待测试结束latch.await();LogUtil.infoWithTimeAndThreadName(任务执行完毕);}/*** 测试线程池 4.0 版本 {link ThreadPool4_0#shutdownWhenNoTask()} 的功能*/Testpublic void testShutdownWhenNoTask() throws InterruptedException {final int taskSize 3;CountDownLatch latch new CountDownLatch(taskSize);ThreadPool4_0 threadPool new ThreadPool4_0(1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(1));LogUtil.infoWithTimeAndThreadName(提交任务前);for (int i 0; i taskSize; i) {int finalI i;threadPool.submit(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}LogUtil.infoWithTimeAndThreadName(正在执行任务 finalI);latch.countDown();});}LogUtil.infoWithTimeAndThreadName(提交任务后);LogUtil.infoWithTimeAndThreadName(调用停止方法前线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(调用停止方法前任务数量是 threadPool.getCurrTaskNum());threadPool.shutdownWhenNoTask();LogUtil.infoWithTimeAndThreadName(调用停止方法后线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(调用停止方法后任务数量是 threadPool.getCurrTaskNum());// 等待任务执行完毕latch.await();LogUtil.infoWithTimeAndThreadName(任务执行完毕);LogUtil.infoWithTimeAndThreadName(执行完任务后线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(执行完任务后任务数量是 threadPool.getCurrTaskNum());}/*** 测试线程池 4.0 版本 {link ThreadPool4_0#shutdownNow()} 的功能*/Testpublic void testShutdownNow() throws InterruptedException {final int taskSize 3;ThreadPool4_0 threadPool new ThreadPool4_0(1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(1));LogUtil.infoWithTimeAndThreadName(提交任务前);for (int i 0; i taskSize; i) {int finalI i;threadPool.submit(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {LogUtil.infoWithTimeAndThreadName(收到中断信号停止任务执行);}LogUtil.infoWithTimeAndThreadName(正在执行任务 finalI);});}LogUtil.infoWithTimeAndThreadName(提交任务后);LogUtil.infoWithTimeAndThreadName(调用停止方法前线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(调用停止方法前任务数量是 threadPool.getCurrTaskNum());ListRunnable taskList threadPool.shutdownNow();LogUtil.infoWithTimeAndThreadName(有 taskList.size() 个任务没有执行);LogUtil.infoWithTimeAndThreadName(调用停止方法后线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(调用停止方法后任务数量是 threadPool.getCurrTaskNum());// 等待任务执行完毕Thread.sleep(taskSize * 1000 100);LogUtil.infoWithTimeAndThreadName(任务执行完毕);LogUtil.infoWithTimeAndThreadName(执行完任务后线程数量是 threadPool.getCurrPoolSize());LogUtil.infoWithTimeAndThreadName(执行完任务后任务数量是 threadPool.getCurrTaskNum());}/*** 测试线程池 4.0 版本 调用停止方法后直接拒绝任务 的功能*/Testpublic void testShutdownRejectTask() {Assertions.assertThrows(RuntimeException.class, () - {final int taskSize 3;ThreadPool4_0 threadPool new ThreadPool4_0(1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue(1));LogUtil.infoWithTimeAndThreadName(提交任务前);for (int i 0; i taskSize; i) {int finalI i;threadPool.submit(() - {try {Thread.sleep(1000);} catch (InterruptedException e) {LogUtil.infoWithTimeAndThreadName(收到中断信号停止任务执行);}LogUtil.infoWithTimeAndThreadName(正在执行任务 finalI);});}LogUtil.infoWithTimeAndThreadName(提交任务后);threadPool.shutdownWhenNoTask();threadPool.submit(() - {});});}
}8. 测试结果
8.1 test
21:21:24 [ main] 提交任务前
21:21:24 [ main] 提交任务后
21:21:25 [thread-pool-0] 正在执行任务0
21:21:26 [thread-pool-0] 正在执行任务1
21:21:27 [thread-pool-0] 正在执行任务2
21:21:27 [ main] 任务执行完毕8.2 testShutdownWhenNoTask
在调用关闭方法后队列中还剩一个任务等待其执行完毕后所有线程退出。
21:22:04 [ main] 提交任务前
21:22:04 [ main] 提交任务后
21:22:04 [ main] 调用停止方法前线程数量是2
21:22:04 [ main] 调用停止方法前任务数量是1
21:22:05 [thread-pool-0] 正在执行任务0
21:22:05 [thread-pool-1] 正在执行任务2
21:22:05 [thread-pool-1] 发现毒丸退出循环
21:22:05 [ main] 调用停止方法后线程数量是1
21:22:05 [ main] 调用停止方法后任务数量是1
21:22:06 [thread-pool-0] 正在执行任务1
21:22:06 [thread-pool-0] 发现毒丸退出循环
21:22:06 [ main] 任务执行完毕
21:22:06 [ main] 执行完任务后线程数量是0
21:22:06 [ main] 执行完任务后任务数量是08.3 testShutdownNow
在调用关闭方法后队列中的一个任务被取出来随后两个线程相继退出。
21:22:22 [ main] 提交任务前
21:22:22 [ main] 提交任务后
21:22:22 [ main] 调用停止方法前线程数量是2
21:22:22 [ main] 调用停止方法前任务数量是1
21:22:22 [thread-pool-1] 收到中断信号停止任务执行
21:22:22 [thread-pool-0] 收到中断信号停止任务执行
21:22:22 [thread-pool-1] 正在执行任务2
21:22:22 [thread-pool-0] 正在执行任务0
21:22:22 [thread-pool-1] 发现毒丸退出循环
21:22:22 [ main] 有1个任务没有执行
21:22:22 [thread-pool-0] 发现毒丸退出循环
21:22:22 [ main] 调用停止方法后线程数量是0
21:22:22 [ main] 调用停止方法后任务数量是0
21:22:25 [ main] 任务执行完毕
21:22:25 [ main] 执行完任务后线程数量是0
21:22:25 [ main] 执行完任务后任务数量是08.4 testShutdownRejectTask
这个测试只要通过就说明抛出异常了即拒绝了提交的任务。
21:22:53 [ main] 提交任务前
21:22:53 [ main] 提交任务后
21:22:54 [thread-pool-0] 正在执行任务0
21:22:54 [thread-pool-1] 正在执行任务2
21:22:54 [thread-pool-1] 发现毒丸退出循环9. 思考
我们在实现让线程池停止的方法时使用了毒丸的设计你了解 ThreadPoolExecutor 使用的设计是什么吗在测试时我发现 testShutdownWhenNoTask 会有死锁的情况出现你知道这是为什么吗
10. 总结
这次我们实现了自定义线程池的 4.0 版本了解了毒丸的思想还在一定程度上顾及了多线程环境下的线程安全问题。以上算是将线程池基本功能都实现了一遍之后会专门写一篇文章用来回答之前没有讲的思考题。