一、简介
在Java并发编程中,“毒丸”指的是将一个对象放在队列当中,当得到这个对象的时候立即停止执行
下面是一个使用“毒丸”来取消任务的一个示例
如图所示,我们假设一个任务从开始到结束需要经历4个步骤,正常情况下4个步骤将会顺序执行。
而在任务的执行过程中,我们由于一些原因需要取消这个任务,那这个时候我们设置一个“毒丸”,每个步骤在执行开始的时候会进行校验,如果遇到“毒丸”那么将终止执行。
考虑到添加“毒丸”的时候,上一个任务可能还在执行中,所以添加毒丸以后,需要对上一个步骤进行清理,例如:上一个步骤是在查询数据库,那么可能你需要把数据库的查询中断掉
二、示例代码
PoisonDemo
public class PoisonDemo { public static void main(String[] args) { // 实例化一个任务 final Task task = new Task(); // 另起一个线程,在2秒以后取消任务 new Thread(new Runnable() { public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } task.cancel(); } }).start(); // 主线程执行任务 task.execute(); } }
Task
public class Task { private String[] stepNames = { "step1", "step2", "step3", "step4" }; private int currentIndex = 0; private TaskCancelManager manager = new TaskCancelManager(); public void execute() { System.out.println("任务开始执行 steps=" + Arrays.asList(stepNames)); Step currStep = null; while (true) { if (currentIndex + 1 > stepNames.length) { break; } // 执行任务 String stepName = stepNames[currentIndex]; boolean success = manager.addStep(stepName); if (!success) { System.out.println(stepName + " 不继续执行"); // 清理上一个步骤的数据 if (currStep != null) { currStep.purge(); } break; } // 获取当前步骤,并执行 currStep = new Step(stepName); currStep.execute(); currentIndex++; } System.out.println("任务执行结束 steps=" + manager.getStepNames()); } public void cancel() { manager.addPoison(); } }
Step
public class Step { private String stepName; public Step(String stepName) { this.stepName = stepName; } public void execute() { System.out.println(stepName + " 开始执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(stepName + " 结束执行"); } public void purge() { System.out.println("任务中断,清理" + stepName + "步骤"); } }
TaskCancelManager:这里采用同步锁来控制添加“毒丸”和“步骤”是互斥的
public class TaskCancelManager { private static final String POISON = "poison"; private List<String> stepNames = new ArrayList<String>(); public synchronized boolean addStep(String stepName) { // 如果上一个是毒丸 if (stepNames.size() > 0 && stepNames.get(stepNames.size() - 1).equals(POISON)) { return false; } stepNames.add(stepName); return true; } public synchronized void addPoison() { stepNames.add(POISON); } public List<String> getStepNames() { return stepNames; } }
我们运行程序,最后输出内容为:
任务开始执行 steps=[step1, step2, step3, step4] step1 开始执行 step1 结束执行 step2 开始执行 step2 结束执行 step3 不继续执行 任务中断,清理step2步骤 任务执行结束 steps=[step1, step2, poison]