创建线程的两种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

一、Runnable接口

先看一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

  • public interface Runnable {
  • public abstract void run();
  • }

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

二、Callable接口

Callable接口位于java.util.concurrent包下,在它里面也只声明了一个方法,只不过这个方法叫做call()。

  • public interface Callable<V> {
  • V call() throws Exception;
  • }

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。Callable接口可以看作是Runnable接口的补充,call方法带有返回值,并且可以抛出异常。

三、FutureTask类

如何获取Callable的返回结果呢?一般是通过FutureTask这个中间媒介来实现的。整体的流程是这样的:
把Callable实例当作参数,生成一个FutureTask的对象,然后把这个对象当作一个Runnable,作为参数另起线程。

3.1 FutureTask的结构

3.2 FutureTask的启动
由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。下面以Thread包装线程方式启动来说明一下。

  • import java.util.concurrent.Callable;
  • import java.util.concurrent.FutureTask;
  • public class Demo {
  • public static void main(String[] args) throws Exception {
  • Callable<Integer> call = new Callable<Integer>() {
  • public Integer call() throws Exception {
  • System.out.println("计算线程正在计算结果...");
  • Thread.sleep(3000);
  • return 1;
  • }
  • };
  • FutureTask<Integer> task = new FutureTask<>(call);
  • Thread thread = new Thread(task);
  • thread.start();
  • System.out.println("main线程干点别的...");
  • Integer result = task.get();
  • System.out.println("从计算线程拿到的结果为:" + result);
  • }
  • }

四、Future接口

FutureTask继承体系中的核心接口是Future。Future的核心思想是:一个方法,计算过程可能非常耗时,等待方法返回,显然不明智。可以在调用方法的时候,立马返回一个Future,可以通过Future这个数据结构去控制方法f的计算过程。
这里的控制包括:
get方法:获取计算结果(如果还没计算完,也是必须等待的)
cancel方法:还没计算完,可以取消计算过程
isDone方法:判断是否计算完
isCancelled方法:判断计算是否被取消

补充:同样是获取线程的计算结果,Java则显得很繁琐,而C语言的实现则简单的多。看下面的例子

假设有两个函数:

  • void * dose_do(void * a) {
  • for (int i = 0; i < 5; i++) {
  • sleep(1);
  • puts("does_do");
  • }
  • return NULL;
  • }
  • void * dose_not(void * a) {
  • for (int i = 0; i < 5; i++) {
  • sleep(1);
  • puts("does_not");
  • }
  • return NULL;
  • }

这两个函数都返回了void指针,因为void指针可以指向存储器中任何数据类型的数据,线程函数的返回类必须是void *。

一、创建线程
创建线程可以使用多种线程库,在此我们使用最流行的一种:POSIX线程库,也叫pthread。必须包含#include <pthread.h>头文件。我们使用pthread_create() 函数创建并运行一个线程,而且每个线程都需要把线程信息保存在一个pthread_t类型的数据中。

  • // 线程对象
  • pthread_t t0;
  • pthread_t t1;
  • if (pthread_create(&t0, NULL, dose_not, NULL) == -1) {
  • error("无法创建线程t0");
  • }
  • if (pthread_create(&t1, NULL, dose_do, NULL) == -1) {
  • error("无法创建线程t1");
  • }

二、获取线程返回值
上边的两个函数将会独立的在线程中运行直到结束,但是我们需要知道这两个函数什么时候结束。可以使用pthread_join()函数等待函数结束,它会接受线程函数的返回值,并保存在一个void *类型的数据中。那么这个函数是如何得知线程结束的呢?当得到线程函数的返回值的时候,就表明线程函数结束了。这也是为什么线程函数必须要有返回值的原因。

  • void *result;
  • if (pthread_join(t0, &result) == -1) {
  • error("无法回收线程t0");
  • }
  • if (pthread_join(t1, &result) == -1) {
  • error("无法回收线程t1");
  • }

我们来看全部代码:

    • #include <stdio.h>
    • #include <pthread.h>
    • #include <stdlib.h>
    • #include <unistd.h>
    • #include <errno.h>
    • #include <string.h>
    • // 错误处理函数
    • void error(char *msg) {
    • fprintf(stderr, "Error: %s %s", msg, strerror(errno));
    • exit(1);
    • }
    • void * dose_not(void * a) {
    • for (int i = 0; i < 5; i++) {
    • sleep(1);
    • puts("does_not");
    • }
    • return NULL;
    • }
    • void * dose_do(void * a) {
    • for (int i = 0; i < 5; i++) {
    • sleep(1);
    • puts("does_do");
    • }
    • return NULL;
    • }
    • int main(int argc, const char * argv[]) {
    • // 线程对象
    • pthread_t t0;
    • pthread_t t1;
    • if (pthread_create(&t0, NULL, dose_not, NULL) == -1) {
    • error("无法创建线程t0");
    • }
    • if (pthread_create(&t1, NULL, dose_do, NULL) == -1) {
    • error("无法创建线程t1");
    • }
    • void *result;
    • if (pthread_join(t0, &result) == -1) {
    • error("无法回收线程t0");
    • }
    • if (pthread_join(t1, &result) == -1) {
    • error("无法回收线程t1");
    • }
    • return 0;
    • }

from: http://www.threadworld.cn/archives/39.html

Java Callable和Future简述的更多相关文章

  1. 基于java callable及future接口解决生产者消费者问题

    这两天复习java线程时,把java里面的线程基本知识点与jdk1.5以后新添加的一些类的使用都了解了一下,借用生产者消费者的问题来将他们实践一下. 题目:(题目在csdn一大牛的空间找的) 生产者- ...

  2. 【Todo】Java Callable和Future学习

    参考这篇文章:http://blog.csdn.net/ghsau/article/details/7451464  还有一个系列<Java多线程>

  3. Java Callable 与 Future

  4. Java Callable接口与Future接口的两种使用方式

    Java Callable.Future的两种使用方式Callable+Futurepublic class Test { public static void main(String[] args) ...

  5. Java Callable Future Example(java 关于Callable,Future的例子)

    Home » Java » Java Callable Future Example Java Callable Future Example April 3, 2018 by Pankaj 25 C ...

  6. Java线程之Callable、Future

    简述 在多线程中有时候我们希望一个线程执行完毕后可以返回一些值,在java5中引入了java.util.concurrent.Callable接口,它类似于Runnable接口,但是Callable可 ...

  7. java多线程系类:JUC线程池:06之Callable和Future(转)

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  8. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  9. Java并发编程:Callable、Future和FutureTask

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

随机推荐

  1. net mvc中angular

    把angular项目整合到.net mvc中   之前的开发选择的是完全舍弃服务端,仅保留最简单web服务器提供angular经打包的静态资源,此外所有的业务与数据请求都访问一个分离的WebApi来实 ...

  2. phpinfo常见配置信息

    在开发过程中,经常碰到比如加载的是哪个配置文件.上传文件大小受限.PHP错误日志文件位置等问题需要快速查找出来并解决,因此总结记录出下面的相关配置. phpinfo - 输出关于 PHP 配置的信息 ...

  3. P2817 宋荣子的城堡

    P2817 宋荣子的城堡一道找规律的题,现在深入追究发现了有趣的东西.1 12 23 94 64显然k^(k-1) 在日照的时候也推出来了.3 9今天推错了,要列出所有的情况,然后再选,否则会漏掉.答 ...

  4. 008.MySQL-Keepalived搭配脚本02

    vim /etc/keepalived/check_MySQL.sh #!/bin/sh #isok=$(sed -n '2p' /etc/keepalived/result.txt) isok=$( ...

  5. Web Service(一):初识

    1. 前言 cxf 在项目中应用好久了,一直没有写总结,现在补上. 由于cxf 属于Web Service的一个实现,所以先学习和总结一下Web Service,作为学习cxf的基础. 2. WebS ...

  6. php版本CKFinder3.4.4自定义上传文件位置

    1.修改文件上传路径: 编辑ckfinder目录下config.php,70行设置为:    'baseUrl'      => '/uploads/'.date('Ymd').'/'; 这样上 ...

  7. BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)

    题目链接 SAM:能成为识别子串的只有那些|right|=1的节点代表的串. 设这个节点对应原串的右端点为r[i],则如果|right[i]|=1,即\(s[\ [r_i-len_i+1,r_i-le ...

  8. SPI中的极性CPOL和相位CPHA

    详解SPI中的极性CPOL和相位CPHA SPI由于接口相对简单(只需要4根线),用途算是比较广泛,主要应用在 EEPROM,FLASH, 实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间 ...

  9. sqlserver 2012 IDE中 Windows身份验证连接服务器报错 ,Login failed for user 'xxx\Administrator'. 原因: 找不到与提供的名称匹配的登录名。

    问题描述: 本地装了两个实例,一个是SQLEXPRESS,可以正常操作.但是另一个开发常用的实例MSSQLSERVER却连Windows身份验证都报错,报的错误也是很奇葩,怎么会找不到Administ ...

  10. 使用position:relative制作下边框下的小三角

    在制作tab选项卡的时候,有时会有下边框,且下边框下另一个头向下的小三角,这全然能够用css来实现,而不必使用背景图片. 由于使用背景图片时会有一个问题,选项卡内容字数不同.导致使用背景图片时无法控制 ...