Future这玩意

异步执行,听起来很抽象,简单的来说,主线程不管这件事了,起了个新的线程去做这件事,那么这件事的执行过程,对于主线程而言,就是异步的。

可以把这件事的执行过程给抽象出来,定义为一个方法,方法的内容就是这件事的具体执行过程。

比如Runnable。

Runnable

Runnable接口只有一个run()方法,run()里面的内容就是这件事执行的过程,怎么去异步呢?

new Thread(new Runnable(){
		@Override
		public void run() {
				// 这件事的逻辑代码
		}
}.start();

如果主线程去执行上面这段代码,那么这件事的执行对于主线程来说,就是异步执行的。

举个例子,小明吃完了饭,是不是得去洗碗呐,如果小明是单身狗,那洗碗这件事没人帮的了小明,所以小明就得花这10几分钟来洗碗,但小明有钱啊,买个洗碗机,洗碗这件事就不用小明这个主线程来做了,当然是用洗碗机这个新线程去洗碗,洗碗这件事对小明来说就是异步的了。

买的第一代的洗碗机,还有有点low,这洗碗机最后会把碗吃掉,对应于run()不会返回洗完的碗,小明还得看着洗碗机洗碗,不能做其他事情,等于主线程调用了洗碗线程.join()。

如果想洗碗机洗完碗后给碗,可用Callable。但暂时还做不到你不用看着洗碗机洗碗。

Callable

Runnable的run()方法是void,Callable的call()方法,返回的是泛型V,在用法上有着很大的区别,Runnable的实现类实例,可以作为Thread对象的参数,也就是Runnable可以与Thread直接关联,Callable要被Thread执行的话,只能转成Runnable。

是你的话,怎么解决Callable与Thread的关联问题?

那我就写个类,实现Runnable接口,把Callable注入进来,然后run()方法就执行Callable对象的call()方法。

哈哈,其实java提供了Callable与Thread的桥梁,FutureTask就是这么一回事,但是呢FutureTask这名字总感觉是Callable和Runnable关系不大…

Callable<Bowl> washBowl = new Callable<Bowl>(){
		@Override
		public Bowl call() {
				// 洗碗这件事,可能会花10分钟,或者15分钟,这个时间对小明来说透明的
		}
}

// 把washBowl这件事转为Thread可执行的事情,还得需要FutureTask这个中间者
FutureTask<Bowl> washBowlMiddler = new FutureTask<>(washBowl);

// 洗碗机去异步地洗碗
Thread washBowlMachine = new Thread(washBowlMiddler).start();

// 怎么拿到洗干净后的碗呢,有个接口叫Future
Bowl cleanBowls = washBowlMiddler.get();

从上面的例子可以看到,对于Callable的结果V,需要借助FutureTask的get()来获取,Callable也蛮可怜的,什么东西都要这个靠中间者来帮忙…

FutureTask的get()也是一个阻塞的方法,这个方法是FutureTask继承于Future接口的get()方法,所以FutureTask = 实现了Callable + 实现了Runnbale + 实现Future 。到这好像能知道FutureTask这个名字的由来了,首先他包含了要做的事情(Task),还可以拿到异步执行事情的结果(Future)。

所以最后一行代码,主线程会阻塞在那,等于小明还是只能看着洗碗机洗10分钟或者更久,但好处是小明可以拿到洗干净的碗。

那我又想要洗干净的碗,又想不盯着洗碗机,可以躺床上打游戏,那可以用Guava。

Guava

第三代洗碗机终于发布,小明直呼好家伙,直接入手,这下可以不用盯着洗碗机洗完了,还能给出干净的碗。

想想看,为什么小明之前要傻傻的阻塞在那盯着洗碗机把碗洗完?因为小明不知道洗碗机会洗多久,第三代改良后,洗碗机在洗完后,会哔哔哔的叫,这时候小明就知道碗原来洗好了。

在Guava中,有几个组件需要提前去了解下,

  • FutureCallback,这个接口名字里包含callback,大概知道是回调的东西,还带Future,那应该也能知道是异步执行的东西,所以这组件就是异步执行结果的回调,要注意的是,这个结果可能是执行成功的结果,也可能是执行出错的结果,所以FutureCallback有两个方法:

void onSuccess(@Nullable V var1); // 成功的结果,那就传入成功的结果

void onFailure(Throwable var1); // 失败的结果,那就传失败的原因

   说白了,就是做善后处理的接口。
  • ListenableFuture,名字也带有Future,真的和Future过不去了…,姑且就把这个当做是 FutrueTask的加强版,FutureTask执行完后的结果想要执行到上面的FutureCallback善后工作,就得和它关联起来,所以ListenableFuture就由一个addListener(Runnable r, Executor e)方法来进行关联,很明显,这个方法是观察者模式,至于ListenableFuture怎么合FutureCallback如何关联起来,Guava也提供了专门的步骤:
Futures.addCallback(listenableFuture, new FutureCallBack<Bowl>() {
		@override
		public void onSuccess(Bowl cleanBowls) {
				// 洗碗机洗完了,传进来干净的碗
		}

		@Override
		public void onFailure(Throwable t) {
				// 洗碗机故障了,传进来故障的对象
		}
}

可类比下支付的回调。
现在看起来还比较抽象,没事,我也觉得抽象,所以明天再写。

<think>我们被要求回答如何停止由ExecutorService.submit()提交的线程。根据引用[1]和[2],我们知道ExecutorService.submit()返回一个Future对象,这个Future对象可以用来管理任务(包括取消任务)。同时,引用[3]中也提到了Future接口以及如何关闭ExecutorService。 关键点:停止线程通常有两种方式: 1. 通过Future.cancel()方法来取消单个任务。 2. 通过ExecutorService.shutdown()或shutdownNow()来关闭整个线程池,从而停止所有任务。 但是,需要注意的是,停止线程的最佳实践是协作式的中断,即任务中需要检查中断状态并响应中断。 具体步骤: 1. 使用Future.cancel(boolean mayInterruptIfRunning)方法: - 如果任务还没开始,则取消任务,不再运行。 - 如果任务正在运行,参数mayInterruptIfRunning为true时,会尝试中断任务(通过调用线程的interrupt()方法)。 2. 任务内部需要响应中断:在任务代码中,需要检查中断状态(Thread.interrupted())或者捕获InterruptedException,并做相应的清理工作,然后退出。 注意:如果任务不响应中断,那么即使调用了cancel(true)也无法停止任务。 示例代码: 假设我们有一个长时间运行的任务,我们希望在某个时刻取消它。 ```java import java.util.concurrent.*; public class CancelTaskExample { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<?> future = executor.submit(() -> { // 任务代码 try { // 模拟长时间运行 while (!Thread.currentThread().isInterrupted()) { // 做一些工作,比如计算 System.out.println("Working..."); Thread.sleep(1000); // sleep会响应中断,抛出InterruptedException } } catch (InterruptedException e) { // 捕获到中断异常,说明被要求中断 System.out.println("任务被中断"); // 恢复中断状态(因为捕获中断异常后会清除中断状态) Thread.currentThread().interrupt(); } System.out.println("任务结束"); }); try { // 让主线程运行一段时间,比如3秒 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 取消任务,参数true表示如果任务正在运行则中断线程 boolean canceled = future.cancel(true); System.out.println("任务取消状态: " + canceled); // 关闭线程池 executor.shutdown(); } } ``` 在上面的例子中,任务是一个循环,每次循环检查中断状态,并且在循环中调用了Thread.sleep()(该方法会响应中断)。当我们在主线程中调用future.cancel(true)时,会向任务线程发出中断信号。任务线程在sleep时会被中断并抛出InterruptedException,或者在下次循环条件检查时发现中断状态而退出循环。 另外,如果任务中没有可响应中断的方法(如sleep、wait等),那么就需要在任务中主动检查中断状态(通过Thread.interrupted()或Thread.currentThread().isInterrupted()),并在检测到中断时退出。 重要:任务必须设计为可中断的,否则无法被取消。因此,在编写任务时,应该考虑中断支持。 另外,关闭整个线程池: 我们可以使用shutdownNow()方法尝试停止所有正在执行的任务,停止处理等待的任务,并返回等待执行的任务列表。该方法会向所有工作线程发送中断信号。同样,任务也需要响应中断才能被停止。 示例: ```java executor.shutdownNow(); // 尝试停止所有正在执行的任务 ``` 总结: 1. 使用Future.cancel(true)来取消单个任务,并配合任务中的中断检查。 2. 使用ExecutorService.shutdownNow()来停止所有任务(同样需要任务响应中断)。 3. 任务中必须正确处理中断,否则无法被取消。 引用[3]中提到了关闭线程池的方法,其中也涉及了awaitTermination和shutdownNow的配合使用,但那是关闭整个线程池,而我们这里主要关注如何停止单个任务。 因此,回答用户:停止由ExecutorService.submit()提交的线程,可以通过调用返回的Future对象的cancel(true)方法,并确保任务代码能够响应中断。 最后,生成相关问题: §§相关问题§§ 1. 如何设计可响应中断的任务? 2. Future.cancel()方法中参数mayInterruptIfRunning设置为false会有什么效果? 3. 如何安全地关闭线程池? 4. 如果任务不响应中断,如何强制停止?</think>### 如何停止由 `ExecutorService.submit()` 提交的线程 在 Java 并发编程中,停止通过 `ExecutorService.submit()` 提交的任务需要结合 `Future` 接口实现。核心机制是**协作式中断**,即任务必须主动响应中断信号才能被安全停止。以下是具体实现方法: --- ### 1. 使用 `Future.cancel()` 停止单个任务 ```java ExecutorService executor = Executors.newFixedThreadPool(2); Future<?> future = executor.submit(() -> { while (!Thread.currentThread().isInterrupted()) { // 检查中断标志 // 执行任务逻辑 System.out.println("Running..."); try { Thread.sleep(1000); // 可响应中断的方法 } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重置中断状态 System.out.println("Task interrupted"); break; // 退出循环 } } }); // 3秒后停止任务 Thread.sleep(3000); future.cancel(true); // true: 尝试中断线程 executor.shutdown(); ``` **关键点**: - `future.cancel(true)` 发送中断信号 - 任务中需循环检查 `Thread.currentThread().isInterrupted()` - 遇到 `InterruptedException` 时重置中断状态并退出[^1][^2] --- ### 2. 使用 `ExecutorService.shutdownNow()` 停止所有任务 ```java ExecutorService executor = Executors.newFixedThreadPool(2); executor.submit(/* 任务1 */); executor.submit(/* 任务2 */); // 立即停止所有任务 List<Runnable> unfinished = executor.shutdownNow(); System.out.println("未完成任务数: " + unfinished.size()); ``` **特性**: - 返回未开始执行的任务列表 - 向所有工作线程发送 `interrupt()` 信号 - 已提交但未执行的任务会被取消[^3] --- ### 3. 强制停止不响应中断的任务(不推荐) 若任务忽略中断信号,可通过标志位强制停止: ```java volatile boolean stopFlag = false; // 易失性标志位 Future<?> future = executor.submit(() -> { while (!stopFlag) { // 检查自定义标志 // 不可中断的阻塞操作 } }); // 停止任务 stopFlag = true; future.cancel(true); // 双保险 ``` > ⚠️ **注意**:此方法需确保 `stopFlag` 为 `volatile` 变量,避免可见性问题。 --- ### 4. 最佳实践总结 | 场景 | 方法 | 注意事项 | |--------------------------|-------------------------------|-------------------------------------| | **停止单个任务** | `future.cancel(true)` | 任务需检查中断状态 | | **停止所有任务** | `executor.shutdownNow()` | 返回未执行任务列表 | | **处理阻塞操作** | 捕获 `InterruptedException` | 需重置中断状态 `Thread.currentThread().interrupt()` | | **任务不响应中断** | 自定义 `volatile` 标志位 | 需考虑内存可见性问题 | > **协作式中断原则**:Java 没有强制停止线程的安全方法,任务必须主动响应中断才能被终止[^1][^3]。 ```mermaid graph LR A[提交任务] --> B[获取Future对象] B --> C{需要停止?} C -- 是 --> D[调用future.cancel(true)] D --> E[任务检查中断状态] E --> F[任务清理资源并退出] C -- 否 --> G[任务正常完成] ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值