Cancel, Reject tasks in ThreadPool

线程池任务取消与拒绝
本文介绍如何在Java中使用线程池取消正在执行的任务和拒绝提交的新任务。通过Future对象的cancel方法取消任务,并利用RejectedExecutionHandler接口处理拒绝执行的任务。

简介

    在前面的文章中我们讨论过提交task到线程池中间执行。那里主要是考虑任务提交后一切都正常执行的情况。但是,如果在某些情况下如果我们想要取消或者拒绝task的提交和执行。则需要用到一些其他的特性。

Cancel Task

    Cancel task在线程池里有比较方便的支持。我们提交task的时候,一般会期待线程池的执行返回Future<T>类型的结果。在执行线程池的submit方法后我们就可以取得Future<T>结果的引用了。所以如果我们想要取消运行的话,可以直接调用该引用的cancel方法。

    我们来看一个具体的示例:

    首先我们定义一个可以提交给线程池的task,它需要实现Callable接口。

import java.util.concurrent.Callable;

public class Task implements Callable<String> {
	@Override
	public String call() throws Exception {
		while(true) {
			System.out.printf("Task: Test\n");
			Thread.sleep(100);
		}
	}
}

     这部分代码里有一个看起来比较诡异的地方。就是我们在call方法的实现里并没有返回任何String类型的结果。它只是一个无限循环。看起来它可能会报错。实际上因为会一直循环下去。代码还是可以通过编译并且运行的。

    然后,我们再来定义使用它的场景代码:

import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

	public static void main(String[] args) {
		ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
		Task task = new Task();
		System.out.printf("Main: Executing the Task\n");
		Future<String> result = executor.submit(task);
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.printf("Main: Canceling the task\n");
		result.cancel(true);
		System.out.printf("Main: Canceled: %s\n", result.isCancelled());
		System.out.printf("Main: Done: %s\n", result.isDone());
		executor.shutdown();
		System.out.printf("Main: The executor has finished\n");
	}
}

     在前面的代码里,我们创建了一个线程池,然后通过submit方法提交task。在sleep两秒钟之后又通过result.cancel(true)方法取消线程的执行。下面是程序的执行结果:

Main: Executing the Task
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Task: Test
Main: Canceling the task
Main: Canceled: true
Main: Done: true
Main: The executor has finished

 

Reject Task

    在前面cancel task的场景中,我们是基于一个thread pool可以接收task并且能够执行。在某些情况下thread pool可能会拒绝task的执行。一般来说,当我们使用线程池结束的时候会调用线程池的shutdown方法。这个方法不会马上结束,它会等待里面的线程执行结束之后再结束。在这个时候,如果再向线程池提交task的话会被线程池拒绝。在thread pool里面提供了一种机制,当提交的task被拒绝之后,它将会被调用。

     在线程池里有一个setRejectedExecutionHandler,这个方法相当于一个事件的注册机制。它接收RejectedExecutionHandler类型的参数。在线程被reject之后该接口的rejectedExecution方法会被触发。

    我们现在来看看具体的实现示例:

    首先,实现一个继承RejectedExecutionHandler的类:

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;


public class RejectedTaskController implements RejectedExecutionHandler {
	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		System.out.printf("RejectedTaskController: The task %s has" +
				"been rejected\n", r.toString());
		System.out.printf("RejectedTaskController: %s\n", executor.toString());
		System.out.printf("RejectedTaskController: Terminating: %s\n", 
				executor.isTerminating());
		System.out.printf("RejectedTaskController: Terminated: %s\n", 
				executor.isTerminated());
	}
}

     然后定义一个提交的线程:

import java.util.concurrent.TimeUnit;


public class Task implements Runnable {
	private String name;
	public Task(String name) {
		this.name = name;
	}
	
	@Override
	public void run() {
		System.out.printf("Task " + name + ": Starting");
		
		try {
			long duration = (long)(Math.random() * 10);
			System.out.printf("Task %s: ReportGenerator: Generating " +
					"a report during %d seconds\n", name, duration);
			TimeUnit.SECONDS.sleep(duration);
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.printf("Task %s: Ending\n", name);
	}
	
	public String toString() {
		return name;
	}
}

   该线程只是打印一些相关的信息并sleep几秒钟。

    具体调用实现的方法如下:

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Main {

	public static void main(String[] args) {
		RejectedTaskController controller = 
				new RejectedTaskController();
		ThreadPoolExecutor executor = 
				(ThreadPoolExecutor) Executors.newCachedThreadPool();
		executor.setRejectedExecutionHandler(controller);
		System.out.printf("Main: Starting.\n");
		for(int i = 0; i < 3; i++) {
			Task task = new Task("Task" + i);
			executor.submit(task);
		}
		
		System.out.printf("Main: Shutting down the Executor.\n");
		executor.shutdown();
		
		System.out.printf("Main: Sending another Task.\n");
		Task task = new Task("RejectedTask");
		executor.submit(task);
		System.out.printf("Main: End.\n");
	}
}

    前面启动了3个线程让他们正常执行。然后再调用线程池的shutdown方法来中止线程池的运行。后面当我们再尝试提交线程的时候,我们可以看到rejectedExecution方法被触发。 

     下面是程序执行的结果:

Main: Starting.
Task Task0: StartingMain: Shutting down the Executor.
Task Task2: StartingTask Task1: StartingMain: Sending another Task.
RejectedTaskController: The task java.util.concurrent.FutureTask@18aa5e75 hasbeen rejected
Task Task1: ReportGenerator: Generating a report during 2 seconds
Task Task2: ReportGenerator: Generating a report during 8 seconds
Task Task0: ReportGenerator: Generating a report during 3 seconds
RejectedTaskController: java.util.concurrent.ThreadPoolExecutor@4b26f29f[Shutting down, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
RejectedTaskController: Terminating: true
RejectedTaskController: Terminated: false
Main: End.
Task Task1: Ending
Task Task0: Ending
Task Task2: Ending

 

总结

    对于提交给线程池执行的线程来说,如果我们希望对线程的执行有更多的控制,比如说取消或者拒绝线程的执行。我们可以通过取得的Future<T>引用调用cancel方法来实现。另外,在线程池已经调用shutdown方法之后,我们继续提交的task会被拒绝。为了知晓线程提交后被拒绝的消息以及做后续的处理,我们可以通过实现接口RejectedExecutionHandler,并注册该实现到线程池中。

参考材料

Java 7 concurrency cookbook

<think>我们正在处理一个关于JavaScript中"Uncaught (in promise) cancel"错误的问题,具体出现在使用ElMessageBox.confirm时(Element UI或类似库的确认框)。这个错误通常是因为在确认框被取消(用户点击取消按钮)时,Promise被拒绝(reject),但没有相应的错误处理(.catch或try/catch)来捕获这个拒绝。 在Element UI中,ElMessageBox.confirm会返回一个Promise。当用户点击“确定”时,Promise解析(resolve);点击“取消”或关闭对话框时,Promise拒绝(reject)。如果没有处理这个拒绝,就会在控制台看到"Uncaught (in promise) cancel"(或类似的错误)。 解决方案: 1. 使用`.catch`捕获拒绝。 2. 使用`async/await`配合`try/catch`。 示例代码(Vue + Element UI): ```javascript // 使用.catch this.$confirm(&#39;确定执行此操作吗?&#39;, &#39;提示&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }).then(() => { // 用户点击确定,执行操作 }).catch((action) => { // 用户点击取消,这里捕获拒绝 // 注意:我们不需要处理错误,因为取消是正常操作,所以可以空着,或者打印日志 if (action === &#39;cancel&#39;) { console.log(&#39;用户取消了操作&#39;); } }); // 或者使用async/await async function confirmAction() { try { await this.$confirm(&#39;确定执行此操作吗?&#39;, &#39;提示&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }); // 用户点击确定,执行操作 } catch (error) { // 这里捕获取消操作 if (error === &#39;cancel&#39;) { console.log(&#39;用户取消了操作&#39;); } } } ``` 注意:在Element UI中,ElMessageBox.confirm被取消时,拒绝的原因(reject reason)是字符串&#39;cancel&#39;(对于确认框)或&#39;close&#39;(如果用户点击了关闭按钮)。因此,在catch中可以检查这个字符串。 另外,在Vue组件中,如果使用this.$confirm,确保在组件销毁时不会还有未处理的Promise(比如在异步操作中,组件销毁前应取消这些操作),否则可能导致内存泄漏或错误。 如果不想在控制台看到错误,一定要捕获拒绝。即使你不需要对取消做任何操作,也应该添加一个空的catch块,以防止未处理的Promise拒绝。 所以,针对问题"ElMessageBox.confirm Uncaught (in promise) cancel",核心就是添加错误处理。 现在,根据规则,我们需要在回答后提出3-5个相关问题。</think>### 回答 "ElMessageBox.confirm Uncaught (in promise) cancel" 错误发生在使用 Element UI 的 ElMessageBox.confirm 组件时,用户点击取消按钮导致 Promise 被拒绝(reject),但未正确处理拒绝状态。这是 Element UI 的预期行为: - **确认** → Promise 解析(resolve) - **取消/关闭** → Promise 拒绝(reject)并返回 `&#39;cancel&#39;` 错误 #### 错误原因 1. 未使用 `.catch()` 或 `try/catch` 处理取消操作 2. 将 ElMessageBox.confirm 直接用在 `async` 函数中但未捕获错误 3. 在 Vue 生命周期钩子(如 `created`)中未处理异步拒绝 #### 解决方案 1. **使用 `.catch()` 显式处理取消**: ```javascript ElMessageBox.confirm(&#39;确定执行操作?&#39;, &#39;提示&#39;, { type: &#39;warning&#39; }) .then(() => { // 用户点击确认 console.log(&#39;执行操作&#39;) }) .catch(action => { if (action === &#39;cancel&#39;) { console.log(&#39;用户取消操作&#39;) // 正常处理取消 } }) ``` 2. **在 `async/await` 中使用 `try/catch`**: ```javascript async function handleConfirm() { try { await ElMessageBox.confirm(&#39;确定执行操作?&#39;, &#39;提示&#39;) // 用户点击确认 } catch (error) { if (error === &#39;cancel&#39;) { console.log(&#39;操作已取消&#39;) // 处理取消 } else { console.error(&#39;其他错误:&#39;, error) } } } ``` 3. **忽略取消操作(静默处理)**: ```javascript ElMessageBox.confirm(&#39;确定删除?&#39;, &#39;警告&#39;) .then(() => { /* 确认逻辑 */ }) .catch(() => {}) // 空catch忽略所有取消 ``` #### 关键点 - ElMessageBox 拒绝时返回 `&#39;cancel&#39;`(取消按钮)或 `&#39;close&#39;`(关闭图标) - 在 Vue 组件中,建议在方法内调用而非直接在生命周期钩子中使用 - 使用 TypeScript 时可声明错误类型: ```typescript .catch((action: &#39;cancel&#39; | &#39;close&#39;) => { ... }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值