关于多线程,我们接触对多的,最基础,入门的可能就是实现Runnable接口继承Thead类,因为Java单继承的原因,通常建议是实现Runnable接口。但这种“简单”的线程会带来一个问题,写过的人都知道,不管是实现Runnable还是继承Thread类,他们都是实现public void run()方法,而这个方法可以看到是没有返回值的。但我们想执行一个线程得到它的返回值应该怎么做呢?此时FutureTask就要出场了。
首先我们还是需要定义一个线程,不同的是这个线程需要实现Callable接口。这是第一个不同的地方。
package futuretask; import java.util.concurrent.Callable; /**
* Created by yulinfeng on 12/17/16.
*/
public class Task implements Callable<String> {
private String name; public Task(String name){
this.name = name;
} @Override
public String call() throws Exception {
String hello = hello(name);
return hello;
} public String hello(String name){
return "hello " + name;
}
}
可以看到此时并不是实现run方法,而是一个叫call的方法,同时有返回值(返回值取决于所定义的泛型)。定义好这个线程过后,并不是直接将此线程的实例传入一个Thread类中执行start方法,这也是和没有返回值的线程不同的地方。
下面我们通过两种方法使用FutureTask,一个是通过线程池的方式创建线程,一个是直接创建一个线程。
通过多线程的方式创建线程:
package futuretask; import countdownlatch.TaskThread; import java.util.concurrent.*; /**
* Created by yulinfeng on 12/17/16.
*/
public class Test {
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool(); //创建一个线程池
Future<String> hello = exec.submit(new Task("kevin")); //执行线程池的submit方法,执行callable线程,并返回一个Future对象
try {
System.out.println(hello.get()); //通过调用Future的get方法,获取线程的返回值
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
这是第一种通过线程池创建线程的方式,通过调用线程的submit方法。如果是实现的Runnable方法,通过线程池创建线程执行的则是线程池的execute方法。一定注意调用线程返回值时使用的是Future的get方法来获取的返回值。
直接创建线程:
package futuretask; import countdownlatch.TaskThread; import java.util.concurrent.*; /**
* Created by yulinfeng on 12/17/16.
*/
public class Test {
public static void main(String[] args){
FutureTask<String> future = new FutureTask<String>(new Task("kevin")); //将线程传入FuturaTask的构造方法中
Thread thread = new Thread(future);
thread.start();
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
在调用get方法时,一定是在执行完线程后,不然将会一直阻塞。
其实仔细观察以上两张方法,第一种是Future接口,第二种是FutureTask类。FutureTask是Future接口的实现,为什么不能将Future接口传入Thread的构造方法中呢?原因很简单。在Thread的单个参数构造方法中,只能传入Runnable类型,而Future并没有继承Runnable接口,FutureTask则同时继承了Runnable接口和Future接口。
最后,那么FutureTask应该怎么来定义呢?其实和我们上一节将的CountDownLatch一样,它也是一种闭锁的实现。Future.get取决于任务的状态,如果任务已完成,那么get会立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常。FutureTask将计算结果从执行计算的线程传递到获取这个结果的线程,而FutureTask的规范确保了这种传递过程能实现结果的安全发布。——《Java并发编程实践》
FutureTask——另一种闭锁的实现的更多相关文章
-
并发编程-Future+callable+FutureTask 闭锁机制
项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable实现. FutureTask ...
-
深入浅出 Java Concurrency (10): 锁机制 part 5 闭锁 (CountDownLatch)
此小节介绍几个与锁有关的有用工具. 闭锁(Latch) 闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻 ...
-
深入浅出 Java Concurrency (10): 锁机制 part 5 闭锁 (CountDownLatch)[转]
此小节介绍几个与锁有关的有用工具. 闭锁(Latch) 闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻 ...
-
jdk1.8 J.U.C之FutureTask实现机制分析
我画了一张关于FutureTask的类图,主要包括FutureTask的几个重要的函数和字段,还有它和父类的关系. 根据上面图我们可以清晰的看出FutureTask的继承关系.FutureTask继承 ...
-
CountDownLatch——闭锁的实现之一
CountDownLatch实际上是一种闭锁实现.闭锁:是一种同步工具类,可以延迟线程的进度知道其到达终止状态--<Java并发编程实战>.这个怎么解释呢?简单来说,就是有1个线程需要等待 ...
-
FutureTask理解
一.概述 FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口. 类图如下: FutureTask是一种可以取消的异步的计算任务.它的计算是通过Callable ...
-
Callable,Future和FutureTask详解
1.Callable和Runnable 看Callable接口: public interface Callable<V> { /** * Computes a result, or th ...
-
【并发编程】【JDK源码】J.U.C--组件FutureTask、ForkJoin、BlockingQueue
原文:慕课网实战·高并发探索(十三):并发容器J.U.C -- 组件FutureTask.ForkJoin.BlockingQueue FutureTask FutureTask是J.U.C中的类,是 ...
-
并发编程—— FutureTask 源码分析
1. 前言 当我们在 Java 中使用异步编程的时候,大部分时候,我们都会使用 Future,并且使用线程池的 submit 方法提交一个 Callable 对象.然后调用 Future 的 get ...
随机推荐
-
MySQL乱码问题
JSP的request 默认为ISO8859_1,所以在处理中文的时候, 要显示中文的话,必须转成GBK的,如下 String str=new String(request.getParamete ...
-
渐析java的浅拷贝和深拷贝
首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝. 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所 ...
-
Android 采用Layout Inflater创建一个View对象
接着上文<Android ListViewview入门>,本文使用android的Inflater来实现 在layouyt文件夹中新建一个list_item.xml的文件,添加如下代码: ...
-
linux 下 mysql 安装(不用编译的方式)
环境是centos6.x.相信其他的也大同小异.相对来说这种方式我用的比较多. 一些环境依赖表库: yum install perl yum install libaio 1)下载:在mysql的网站 ...
-
使用selenium实现右键另存为保存文件
1.需要借住autoit工具和Robot类,下载地址:https://www.autoitscript.com/site/autoit/downloads/ 2.autoit的使用不再详细讲解.如下图 ...
-
java 16 -11 ArrayList存储自定义对象并增强for遍历
需求:ArrayList存储自定义对象并遍历.要求加入泛型,并用增强for遍历. A:迭代器 B:普通for C:增强for LinkedList,Vector,Colleciton,List ...
-
zookeeper,win版本搭建集群遇坑记录
版本:3.4.10 问题:启动 zkServer.cmd时报错如下, 解决办法: bin目录下 zkEnv.cmd配置 修改为: 把双引号放在外面. 此时运行zkServer.cmd 用zkCli.c ...
-
Go基础之--反射
反射:可以在运行时动态获取变量的相关信息 反射需要导入reflect 反射中重要函数的演示 反射有几下几个重要的函数:reflect.TypeOf :获取变量的类型,返回reflect.Type类型r ...
-
VS2017简单使用
1. 2.删除下面的文件 3.点击属性 4.改为否 不使用预编译头 万能头文件自己导入网上有教程
-
java中boolean与字符串或者数字1和0的转换
mysql有个字段是bit,只存储1和0,是二进制存储,那么在java的dao层如何映射成boolean呢 @Column(name="is_standard") private ...