前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FutureTask——另一种闭锁的实现

FutureTask——另一种闭锁的实现

作者头像
用户1148394
发布2018-01-09 17:37:24
7070
发布2018-01-09 17:37:24
举报
文章被收录于专栏:余林丰余林丰

关于多线程,我们接触对多的,最基础,入门的可能就是实现Runnable接口继承Thead类,因为Java单继承的原因,通常建议是实现Runnable接口。但这种“简单”的线程会带来一个问题,写过的人都知道,不管是实现Runnable还是继承Thread类,他们都是实现public void run()方法,而这个方法可以看到是没有返回值的。但我们想执行一个线程得到它的返回值应该怎么做呢?此时FutureTask就要出场了。

首先我们还是需要定义一个线程,不同的是这个线程需要实现Callable接口。这是第一个不同的地方。

代码语言:javascript
复制
 1 package futuretask;
 2 
 3 import java.util.concurrent.Callable;
 4 
 5 /**
 6  * Created by yulinfeng on 12/17/16.
 7  */
 8 public class Task implements Callable<String> {
 9     private String name;
10 
11     public Task(String name){
12         this.name = name;
13     }
14 
15     @Override
16     public String call() throws Exception {
17         String hello = hello(name);
18         return hello;
19     }
20 
21     public String hello(String name){
22         return "hello " + name;
23     }
24 }

可以看到此时并不是实现run方法,而是一个叫call的方法,同时有返回值(返回值取决于所定义的泛型)。定义好这个线程过后,并不是直接将此线程的实例传入一个Thread类中执行start方法,这也是和没有返回值的线程不同的地方。

下面我们通过两种方法使用FutureTask,一个是通过线程池的方式创建线程,一个是直接创建一个线程。

通过多线程的方式创建线程:

代码语言:javascript
复制
 1 package futuretask;
 2 
 3 import countdownlatch.TaskThread;
 4 
 5 import java.util.concurrent.*;
 6 
 7 /**
 8  * Created by yulinfeng on 12/17/16.
 9  */
10 public class Test {
11     public static void main(String[] args){
12         ExecutorService exec = Executors.newCachedThreadPool();     //创建一个线程池
13         Future<String> hello = exec.submit(new Task("kevin"));  //执行线程池的submit方法,执行callable线程,并返回一个Future对象
14         try {
15             System.out.println(hello.get());    //通过调用Future的get方法,获取线程的返回值
16         } catch (InterruptedException e) {
17             e.printStackTrace();
18         } catch (ExecutionException e) {
19             e.printStackTrace();
20         }
21     }
22 }

这是第一种通过线程池创建线程的方式,通过调用线程的submit方法。如果是实现的Runnable方法,通过线程池创建线程执行的则是线程池的execute方法。一定注意调用线程返回值时使用的是Future的get方法来获取的返回值。

直接创建线程:

代码语言:javascript
复制
 1 package futuretask;
 2 
 3 import countdownlatch.TaskThread;
 4 
 5 import java.util.concurrent.*;
 6 
 7 /**
 8  * Created by yulinfeng on 12/17/16.
 9  */
10 public class Test {
11     public static void main(String[] args){
12         FutureTask<String> future = new FutureTask<String>(new Task("kevin"));  //将线程传入FuturaTask的构造方法中
13         Thread thread = new Thread(future); 
14         thread.start();
15         try {
16             System.out.println(future.get());
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         } catch (ExecutionException e) {
20             e.printStackTrace();
21         }
22     }
23 }

在调用get方法时,一定是在执行完线程后,不然将会一直阻塞。

其实仔细观察以上两张方法,第一种是Future接口,第二种是FutureTask类。FutureTask是Future接口的实现,为什么不能将Future接口传入Thread的构造方法中呢?原因很简单。在Thread的单个参数构造方法中,只能传入Runnable类型,而Future并没有继承Runnable接口,FutureTask则同时继承了Runnable接口和Future接口。

最后,那么FutureTask应该怎么来定义呢?其实和我们上一节将的CountDownLatch一样,它也是一种闭锁的实现。Future.get取决于任务的状态,如果任务已完成,那么get会立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常。FutureTask将计算结果从执行计算的线程传递到获取这个结果的线程,而FutureTask的规范确保了这种传递过程能实现结果的安全发布。——《Java并发编程实践》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-12-18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档