带你进入java.util.concurrent.Future


1.概览

在这篇文章中,我们将要学习一下Future。这个接口自从java1.5就已经提供了,它在处理异步调用和并发处理时,十分有用。

2.创建Future

简单地说,Future类代表了一个异步计算的future 结果 - 这个结果最终会在处理完成之后显示在Future中。   让我们来看一下,如何写一个方法去创建并返回一个Future实例。异步处理和Future接口都比较适合用于处理耗时较长的方法。这使得我们在等待Future任务完成的时候,有能力去执行其他的处理。

下面这下例子都是可以发挥出Future的异步特性的:

      .计算性处理(数学和科学运算)

      .操作大数据结构(大数据)

      .远程方法调用(下载文件,html片段,web服务)

    //即: Future主要是用于异步处理的。


2.1 使用FutureTask实现Future

在我们的例子中,我们将创建一个十分简单得类,这个类会计算一个integer值得平方。这肯定不是一个"长时间运行"的方法,但是我们将会对它调用Thread.sleep()方法,让它持续1秒钟完成。

public   class   SquareCalculator {   

    privateExecutorService executor= Executors.newSingleThreadExecutor();

    publicFuture calculate(Integer input) {       

        returnexecutor.submit(() -> {

            Thread.sleep(1000);

            returninput * input;

        });

    }

}

实际执行运算的这小段代码都被包含在call()方法中,作为一个lambda表达式提供。正如你所看到的,这里并没有什么特殊的东西,处理调用了一个sleep()方法。

当我们把注意力转到Callable和ExecutorService的用法时,事情就开启变得有趣啦。

Callable接口代表了一个能返回结果的任务,它有一个单独的call()方法,这里,我们已经使用lambda表达式创建了一个它的实例。

创建一个Callable实例并不会让我们少做工作,我们还是不得不把Callable实例传递给executor,然后这个executor将会负责在一个新线程中启动该任务,同时给回我们一个有值的Future对象。这就是ExecutorService的用武之地。

我们有许多获取ExecutorService实例的方式,它们中的大多数都是以工具类Executor的静态工厂方法被提供出来的,在这个案例中,我们使用的是最基础的newSingleThreadExecutor(),这个方法会给我们一个每一次都能处理一个单线程的ExecutorService实例。

一旦我们拥有了一个ExecutorService对象,我们只需要调用submit()方法,把Callable作为参数传递给它。submit()方法会负责该任务的启动以及返回一个FutureTask对象。这个FutureTask对象是Future接口的一个实现。


3.消费Future

到此刻为止,我们已经学会了如果创建一个Future实例,在这一章节 ,通过探索它都有哪些方法,我们将学会如何使用该实例。在本章节中,我们在探究Future API的所有方法的同时,将学会如何使用它。

3.1 使用isDone和get()方法去获取结果

现在,我们需要调用calculate()方法并且使用返回的Future对象来得到一个Integer的结果值。Future API的两个方法将会帮助我们做到这一点。

Future.isDone()方法会告诉我们executor是否已经处理完该任务,如果任务已经完成了,它就返回true,否则的话,就返回false。

实际返回运算结果的方法是Future.get(),请注意:这个方法会阻塞运行直到任务完成。但是,在我们的案例中,这都不是一个问题,因为我们会先调用isDone()方法来检查该任务是否已经完成。

通过使用这俩个方法,我们可以在等待主任务结束期间,运行一些其他代码:

Future future = new SquareCalculator().calculate(10);

while(!future.isDone()) {

    System.out.println("Calculating...");

    Thread.sleep(300);

   Integer result = future.get();

}

在这个例子中,我们向控制台输出了一个简单的信息,好让用户知道程序此时正在执行计算。

该get()方法将会阻塞住execution直到task任务完成。但是我们不必担心这一点,因为我们的例子中,是在确保task任务已经结束之后,才调用的get()方法。所以,在这个场景下,future.get()方法总会立即返回结果。值得提醒一点的是:get()方法有一个重载版本,这个重载方法会接收一个timeout和一个TimeUnit作为参数。

Integer result = future.get(500, TimeUnit.MILLISECONDS);

get(long, TimeUnit)和get()方法的区别是: 当task任务没有在指定的超时时间内返回的话,前者将会抛出一个TimeoutException异常。

3.2 用cancel()方法来取消一个Future

设想一下,我们已经触发了一个task,但是出于某些原因,我们根据不关系它的结果。这时,我们就可以使用Future.cancel(boolean) 方法去告诉该executor停止操作并且中断它潜在的线程:

Future future = newSquareCalculator().calculate(4);

booleancanceled = future.cancel(true);

上述例子中的Future实例,不会结束他的运算操作。事实上,如果我们试图在调用cancel()方法之后,调用该实例的get()方法的话,将会产生一个CancelllationException异常。Future.isCancelled()将会告诉我们,是否Future已经被取消了。这对于避免CancellationException异常很有帮助。

在我们调用cancel()方法时,是有可能失败的。在这种情况下,它的返回值将是false.请注意:cancel()方法会接收一个Boolean值作为参数-它会控制正在执行该task的线程是否应该被中断。


4.带有线程池的多线程

我们当前的ExecutorService是一个单线程的,因为它是通过Executors.newSingleThreadExecutor()得到的。为了强调它的“single threadness”,我们就同时触发两个运算:

SquareCalculator squareCalculator = newSquareCalculator();

Future future1 = squareCalculator.calculate(10);

Future future2 = squareCalculator.calculate(100);

while(!(future1.isDone() && future2.isDone())) {

    System.out.println(

      String.format(

        "future1 is %s and future2 is %s",

        future1.isDone() ? "done": "not done",

        future2.isDone() ? "done": "not done"

      )

    );

    Thread.sleep(300);

}

Integer result1 = future1.get();

Integer result2 = future2.get();

System.out.println(result1 + " and "+ result2);

squareCalculator.shutdown();

现在,我们来分析一下这段代码的输出:

calculating square for: 10

future1 is not done and future2 is not done

future1 is not done and future2 is not done

future1 is not done and future2 is not done

future1 is not done and future2 is not done

calculating square for: 100

future1 is done and future2 is not done

future1 is done and future2 is not done

future1 is done and future2 is not done

100and 10000

有一点是很清楚,那就是这个处理过程不是并发的。可以注意到,第二个task是在第一个task完成之后才开始的。

为了让我们的程序真正地实现多线程,我们应该使用其他的ExecutorService,我们来看一下,如果我们使用线程池的话,程序会发生哪些变化,这里的线程池是由工厂方法Executors.newFFixedThreadPool()提供的:

public class SquareCalculator {

    private ExecutorService executor = Executors.newFixedThreadPool(2);

    //...

}

现在在我们的SquareCalculator类中做一个简单地变化,这时,我们就得到了一个能同时使用2个线程的executor。

如果我们再次运行相同的客户端代码,我们会得到下面的输出:

calculating square for: 10

calculating square for: 100

future1 is not done and future2 is not done

future1 is not done and future2 is not done

future1 is not done and future2 is not done

future1 is not done and future2 is not done

100 and 10000

现在是不是看起来好多啦,观察一下,这俩个task是如何同时启动和结束的。完成整个的处理过程花费了1秒左右。

此外,还有其他可以用来创建线程池的方法,比如,Executos.newCachedTreadPool()以及Executors.newScheduledThreadPool()。

更多关于ExecutorSerivce的信息,请查看我们关于该主题的专门文章。



5.总结

在这篇文章中,我们以一个广泛的视角看了一下Future接口,访问它的所有方法。我们还学习到了如何运用线程池的力量去触发多并发操作。

ForkJoinTask类中的方法:fork,join都简要的介绍了一下。

本文中的代码,都可以在github上找到,地址为:source code

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,320评论 0 10
  • 个人笔记,方便自己查阅使用 Contents Java LangAssignment, ReferenceData...
    freenik阅读 1,376评论 0 6
  • pt10「王女与女王」 [小E视角] 「她」,并没有出现在我的视线中, 取而代之的是…… 那个我以前最想见到,又最...
    伊丽莎薰云阅读 333评论 2 2
  • 生怕会被抱出群,急急忙忙完作业,画的不好不漂亮,还有漏洞在里边,抱歉抱歉真抱歉,烦请老师多谅解!
    心心竹阅读 192评论 1 1
  • The two mice adjusted themselves to the change and found ...
    Rosaqq阅读 131评论 0 0