为什么80%的码农都做不了架构师?>>>   

上次我们说到了 JUC 中的 Future 接口,在最后提到了 FutureTask、CompletionService 等。我们这次先通过 JCIP 中的示例说说 FutureTask 的基本使用,然后在下次说一说如何通过重载 FutureTask 的 done() 来扩展 FutureTask 的功能。

应用示例:Final implementation of Memoizer

我们在上一篇文章《Java 并发之 Future 接口》中提到了单纯使用 Future 接口的局限性,其中一些可以用 FutureTask 解决。因为我随手在网上搜了一下“FutureTask”,发现排名靠前的例子其实都很不能说明 FutureTask 的作用,所以这里我就窃用一下大师的劳动成果,引用《Java Concurrency in Practice》中的例子,就是上篇中提到的 Listing 5.19,方便没有此书的同学。这里再次建议想学好 Java 并发的同学一定要好好读此书。(读过 JCIP 的同学可以跳过此节)

public class Memoizer<A, V> implements Computable<A, V> {private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();private final Computable<A, V> c;public Memoizer(Computable<A, V> c) {this.c = c;}public V compute(final A arg) throws InterruptedException {while (true) {Future<V> f = cache.get(arg);if (f == null) {Callable<V> eval = new Callable<V>() {public V call() throws InterruptedException {return c.compute(arg);}};FutureTask<V> ft = new FutureTask<V>(eval);f = cache.putIfAbsent(arg, ft);if (f == null) {f = ft;ft.run();}}try {return f.get();} catch (CancellationException e) {cache.remove(arg, f);} catch (ExecutionException e) {throw LaunderThrowable.launderThrowable(e.getCause());}}}
}

代码解析

接下来讲解一下上面那段代码。

先说些题外话,在上面这段代码中,参数和变量的命名都十分的简单,都是几个简单字母组成的。在实际项目的开发中不要使用这样的命名,示例中无妨。

Memoizer 这个类的工作就是缓存计算结果,避免计算工作的重复提交。这是一个示例代码,所以没有保护缓存失效等的逻辑。Memoizer 类实现了 Computable 接口。同时,Memoizer 类构造方法里面也要接受一个 Computable 的实现类作为参数,这个 Computable 实现类将去做具体的计算工作。

Memoizer 的 compute 方法是我们要关注的主要部分。先跳过 while (true),第11行是查看缓存中是否已经存在计算任务,如果没有,新的任务才需要被提交,否则获取结果即可。进入 if (f == null),我们先将 Computable<A, V> c 封装进一个 FutureTask 中。然后调用 ConcurrentMap.putIfAbsent 方法去将计算任务放入缓存。这个方法很关键,因为它是一个原子操作,返回值是 key,所对应的原有的值。如果原有值为空,计算任务才可以被真正启动,否则就会重复执行。最后在 try 中调用计算结果。

抛去 while(true)catch,这段代码很容易理解,因为这些都是正常的流程。接下来说说 while(true),它的作用在于计算任务被取消之后能够再次提交任务。

接下来说两个 catch。第一个是 catch CancellationException,但其实在此示例中,FutureTask 都是本地变量,也都没有调用 cancel 方法,所以程序没有机会执行到这里,所以这块只是起到了示例的作用。

需要注意的是第二个 catch。第二个 catch 捕获的是 ExecutionException,封装在 FutureTask 之内的 Runnable 或 Callable 执行时所抛出的异常都会被封装在 ExecutionException 之中,可以通过 e.getCause() 取的实际的异常。显然,发生 ExecutionException 时,计算显然是没有结果的,而在此示例代码中,异常只是简单地被再次抛出。这会导致计算结果无法取得,而且缓存仍旧被占用,新的计算任务无法被提交。如果 c.compute 是幂等的,那这样做是合理的,因为在此提交的任务还是会导致异常的发生。但如果不是幂等的,比如一些偶然事件,比如网络断开等。这里就需要把计算任务从缓存中移除,使得新的任务可以提交进来。在实际应用中,我们还需要根据具体的异常类型,做不同的处理。如果你不清楚 c.compute 是否是幂等的(因为你无法限制传进来的 Computable 实现有何特性),你可以限制一个重试次数。当重试超过限制,便不再移除缓存。

可直接运行的扩展的 "Final implementation of Memoizer" 代码请见这里

转载于:https://my.oschina.net/lifany/blog/185171

Java 并发之 FutureTask 的基本使用相关推荐

  1. 面试:你说你精通Java并发,给我讲讲Java并发之J.U.C

    转载自 面试:你说你精通Java并发,给我讲讲Java并发之J.U.C J.U.C J.U.C即java.util.concurrent包,为我们提供了很多高性能的并发类,可以说是java并发的核心. ...

  2. java并发之Future与Callable使用

    java并发之Future与Callable使用 这篇文章需要大家知道线程.线程池的知识,尤其是线程池. 有的时候我们要获取线程的执行结果,这个时候就需要用到Callable.Future.Futur ...

  3. JAVA并发之多线程基础(2)

    除了我们经常用的synchronized关键字(结合Object的wait()和notify()使用)之外,还有对应的上篇文章讲到的方法JAVA并发之多线程基础(1)之外,我们日常中使用到最多的也就是 ...

  4. JAVA并发之多线程基础(5)

    上面介绍了并发编程中的栅栏等JAVA并发之多线程基础(4) .通过唯一的一个终点线来帮助确定线程是多晚开始执行下一次操作. LockSupport 提供了一个比较底层的线程挂起操作.有点类似于susp ...

  5. 我的Java开发之路

    最近有一位小伙伴通过公众号给我留言, "我参加工作没多久,看着圈里的技术大牛,特别羡慕,也渴望成为技术大牛,想让您分享一下从小白到大牛是怎样练成的,我该如何提高自己" 首先,谢谢这 ...

  6. java并发之SynchronousQueue实现原理

    前言 SynchronousQueue是一个比较特别的队列,由于在线程池方面有所应用,为了更好的理解线程池的实现原理,笔者花了些时间学习了一下该队列源码(JDK1.8),此队列源码中充斥着大量的CAS ...

  7. 你真的弄明白了吗?Java并发之AQS详解

    你真的弄明白了吗?Java并发之AQS详解 带着问题阅读 1.什么是AQS,它有什么作用,核心思想是什么 2.AQS中的独占锁和共享锁原理是什么,AQS提供的锁机制是公平锁还是非公平锁 3.AQS在J ...

  8. java并发之CopyOnWriteArraySet

    java并发之CopyOnWriteArraySet CopyOnWriteArraySet是基于CopyOnWriteArrayList实现的,持有CopyOnWriteArrayList的内部对象 ...

  9. java并发之CopyOnWirteArrayList

    java并发之CopyOnWirteArrayList CopyOnWirteArrayList的实现 它用了ReentrantLock保证了add,set,remove操作的安全,同时使用volat ...

最新文章

  1. aop框架的一个简单实现
  2. 借助网盘搭建SVN服务器
  3. 现代密码学5.3--Hash and MAC
  4. 独家干货 | 林轩田机器学习课程精炼笔记!
  5. mysql命令行执行复杂sql_mysql命令行中执行sql的几种方式总结
  6. SiteMapCreator 发布 (Open Source)
  7. 吴恩达《机器学习》学习笔记三——多变量线性回归
  8. 全网最细Linux之Centos8安装MySQL8.0以上版本,您值得收藏!
  9. 大数据_MapperReduce_协处理器_类似Mysql的触发器---Hbase工作笔记0024
  10. 服务器LCD显示面板,DELL服务器2950的错误代码表(前LCD面板)
  11. 韩国咖啡连锁店Tom N Toms将发布TomTom代币
  12. [导入]Asp.net 2.0 自定义控件开发[实现自动计算功能(AutoComputeControl)][示例代码下载]...
  13. 开关电源适配器原理_迅为i.MX6ULL开发板原理图分析介绍
  14. vm-tools install for linux
  15. 写给自己的CDSN账号
  16. 计算机公式英汉对照,Excel 2013中英文对照图
  17. 计算机网络经典面试题:在浏览器中输入URL并按下回车后会发生什么?
  18. springboot+Elasticsearch实现word,pdf,txt内容抽取并高亮分词全文检索
  19. c++/qt/opencv实现魔方复原【娱乐】
  20. Babylon.js 深入 - 第 2 章 - 声音(2)

热门文章

  1. 我一定要让所有人都知道awk这个实用操作
  2. linux手误rm可能不需要跑路
  3. JavaScript学习——判断数据类型总结(转)
  4. Cocos2d-x游戏实例-《跑跑跑》制作教程(第二篇)——加入主角
  5. 常用sql语句整理:mysql
  6. require.js的AMD规范详解
  7. PHP 判断点是否在多边形内
  8. ecshop修改后台登陆密码
  9. asmack xmpp应用遇到的问题
  10. 舆情监测系统成为网络利器