线程 sleep 取消_java多线程开发理解Future和FutureTask的使用,任务启动与取消

在多线程开发中,我们经常用到的一个是Thread类,或Runnable接口,但他们不太方便返回执行的结果,所以这里介绍一个新的类,它即能达到多线程异步执行的目标,还能方便的得到结果,这个类就是Callable类。下面我们主要通过代码学习具体的使用,关键部分都在代码中作了注释。

代码:MyCallableTask.java

自定义一个类MyCallableTask,该类继承Callable接口,我们重写call方法,增加一个延时处理让线程sleep2秒的时间,然后返回一个int型数字。

package com.test;

import java.util.concurrent.Callable;

public class MyCallableTask implements Callable {

@Override

public Integer call() throws Exception {

System.out.println("子线程执行中.......");

//下面我们给子线程人为延长2秒的执行时间

Thread.sleep(2000);

int n=0;

for(int i=0;i<100;i++){

n+=i;

}

return n;

}

}

CallableTest.java

下面我们开始主类的编写,因为要执行Callable任务,我们这里把ExecutorService拿出来,它是可以执行Runnable, Callable的线程的。

package com.test;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.Executor;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

import java.util.concurrent.FutureTask;

public class CallableTest {

public static void main(String[] args) {

//实例 化线程池对象

ExecutorService es = Executors.newCachedThreadPool();

MyCallableTask mct = new MyCallableTask();

//使用ExecutorService线程池实例提交callable任务,返回Future对象用于对callable任务进行管理,比如 取消、get等操作

Future fu = es.submit(mct);//使用ExecutorService启动Callable

//使用FutureTask创建一个任务,通过Callable构造实例

FutureTask ft=new FutureTask<>(mct);

es.submit(ft);//使用ExecutorService启动FutureTask

es.shutdown();

System.out.println("主线程开始........");

//我们让主线程等待4秒,令子线程的call任务能执行完,因此才可以使用get获取返回结果

try {

Thread.sleep(4000);

} catch (InterruptedException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

//我们得到submit返回的Future对象,因此下面我们就可以对callable进行cancel、get等操作了

//因为上面主线程等待了4秒,因此到这里子线程已经结束 了,我们下面的cancel操作将会返回false,如果我们将上面的等待时间由3000,改成1000,那样小于子线程的执行时间,因此是返回true

boolean iscan = fu.cancel(true);

if (iscan) {

System.out.println("子线程取消了........");

} else{

try {

System.out.println("第一个任务返回:"+fu.get());

} catch (InterruptedException | ExecutionException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

try {

System.out.println("第二个任务返回:"+ft.get());

} catch (InterruptedException | ExecutionException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

返回结果:

子线程执行中.......

子线程执行中.......

主线程开始........

第一个任务返回:4950

第二个任务返回:4950

b2a5c4cd61b63fe2dea1fd92fd351e45.png

如果我们让主类停留的时间短一点(比子线程短),这时我们下面的代码,取消子线程任务的操作就可以生效了,比如我们把上面的4000,改成1000(也就是停留1秒),看看执行的效果是:

子线程执行中.......

子线程执行中.......

主线程开始........

子线程取消了........

第二个任务返回:4950

6e9cddd89f9785dcb8cf9c82fd87a7fe.png

看到上面的运行情况 了吧,也就是说当我们使用ExecutorService.submit方法提交任务后,我们不仅能拿到结果,还可以在子任务执行完成以前对其进行cancel操作。

至于Future,FutureTask它们的区别在哪里?

我们看上面的代码,我们调用 es.submit(mct)时(mct是一个Callable对象)会返回一个Future对象,这是让我们对Callable进行管理的对象,也使用这个对象进行最后的get获取结果。

我们使用FutureTask时,方法就不太一样了,我们使用es.submit(ft),这里ft是我们前面创建的一个FutureTask对象,es.submit(ft)后,我们并不是像上面那样能得到一个Future,然后使用Future.get获取,这里我们是直接使用ft对象(即FutureTask)的方法获取get。

区别我们可以这么理解

FutureTask是对Future的管理封装,事实上FutureTask继承了Runnable与Future接口。所以就可以理解为我提交Callable的时候,线程池给我返回Future,我用它来操作和获取结果。当提交FutureTask的时候,就是说我要自己管理了,我提交这个任务,不需要返回什么,后面我自己操作和获取结果。

至于原因吗?细心的同学可以研究一下java源码,我们可以从中找到原因:

Executors.newCachedThreadPool()返回的是ThreadPoolExecutor

ThreadPoolExecutor又扩展了抽象类:AbstractExecutorService

最终上面submit的方法就在AbstractExecutorService中

22212184c02719b7e7752df4fc170500.png

AbstractExecutorService.java

在AbstractExecutorService文件中,submit有三个

fea310de2558e7575133899c38b2ccb2.png

看上面代码,当我们提交submit(Callable)时,其实也是将其封装至一个FutureTask对象中了。所以最终意义就是如果我们提交的是Callable对象,那么java的AbstractExecutorService类帮我们new一个FutureTask。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值