本人来自《java多线程实战指南》一书的内容
java多线程停止实际上是一个很复杂的任务,有各种各样的情况,比如:线程还有没有进行完的任务,比如还有处于阻塞状态的任务等等。那么怎么实现有效的停止呢?
就像现实中的跑步一样,飞奔的过程中突然停止总是很难的,总需要一个准备阶段和一个打断的阶段。
我们先来看看线程的运行情况:
那么准备阶段我们需要做什么呢?
1.我们应该告诉线程你要准备停止了,但是如果线程处于IO阻塞状态是获取不到通知的(我们通知只是在应用层面,没法到操作系统层面通知)
我们应该怎么办呢?
我们可以写代码来打断线程。
2.那我们需要等待传输完成才能打断呢?
.join()方法就可以
那么步骤如下:
客户端调用线程拥有者的shutdown方法
shutdown调用目标线程的terminate方法
terminate方法标识为可以打断。
再去调用个性化实现的打断方法。
如果管理的线程没有未处理完的任务或者不关心其是否有未处理的任务或者在停止时不关心其是否有未处理的任务。如果有再次调用打断方法。
1.第一步抽象模型:
在这个场景里面基础模型为,线程,线程可以被打断的标识。线程停止标识的列表清单,主要用来表示那些线程可以被用来停止。
public abstract class AbstractTerminatableThread extends Thread implements Terminatable { final static Logger logger = Logger.getLogger(AbstractTerminatableThread.class); private final boolean DEBUG = true; // 模式角色:Two-phaseTermination.TerminationToken public final TerminationToken terminationToken; public AbstractTerminatableThread() { this(new TerminationToken()); } /** * * @param terminationToken * 线程间共享的线程终止标志实例 */ public AbstractTerminatableThread(TerminationToken terminationToken) { this.terminationToken = terminationToken; terminationToken.register(this); }
2.抽象列表:
public class TerminationToken { // 使用volatile修饰,以保证无需显式锁的情况下该变量的内存可见性 protected volatile boolean toShutdown = false; public final AtomicInteger reservations = new AtomicInteger(0); /* * 在多个可停止线程实例共享一个TerminationToken实例的情况下,该队列用于 * 记录那些共享TerminationToken实例的可停止线程,以便尽可能减少锁的使用 的情况下,实现这些线程的停止。 */ private final Queue<WeakReference<Terminatable>> coordinatedThreads; public TerminationToken() { coordinatedThreads = new ConcurrentLinkedQueue<WeakReference<Terminatable>>(); }
两阶段停止的核心逻辑:
@Override public void terminate() { terminationToken.setToShutdown(true); try { doTerminiate(); } finally { // 若无待处理的任务,则试图强制终止线程 if (terminationToken.reservations.get() <= 0) { super.interrupt(); } } } public void terminate(boolean waitUtilThreadTerminated) { terminate(); if (waitUtilThreadTerminated) { try { this.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }
父类线程处理逻辑主要有:
@Override public void run() { Exception ex = null; try { for (;;) { // 在执行线程的处理逻辑前先判断线程停止的标志。 if (terminationToken.isToShutdown() && terminationToken.reservations.get() <= 0) { break; } doRun(); } } catch (Exception e) { // 使得线程能够响应interrupt调用而退出 ex = e; if (e instanceof InterruptedException) { if (DEBUG) { logger.debug(e); } } else { logger.error("", e); } } finally { try { doCleanup(ex); } finally { terminationToken.notifyThreadTermination(this); } } }