Java并发编程实践 目录

并发编程 01—— ThreadLocal

并发编程 02—— ConcurrentHashMap

并发编程 03—— 阻塞队列和生产者-消费者模式

并发编程 04—— 闭锁CountDownLatch 与 栅栏CyclicBarrier

并发编程 05—— Callable和Future

并发编程 06—— CompletionService : Executor 和 BlockingQueue

并发编程 07—— 任务取消

并发编程 08—— 任务取消 之 中断

并发编程 09—— 任务取消 之 停止基于线程的服务

并发编程 10—— 任务取消 之 关闭 ExecutorService

并发编程 11—— 任务取消 之 “毒丸”对象

并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性

并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略

并发编程 14—— 线程池 之 整体架构

并发编程 15—— 线程池 之 原理一

并发编程 16—— 线程池 之 原理二

并发编程 17—— Lock

并发编程 18—— 使用内置条件队列实现简单的有界缓存

并发编程 19—— 显式的Conditon 对象

并发编程 20—— AbstractQueuedSynchronizer 深入分析

并发编程 21—— 原子变量和非阻塞同步机制

概述

第1部分 Callable

第2部分 Future

第3部分 示例和源码分析

  3.1 submit()

  3.2 FutureTask的构造函数

  3.3 FutureTask的run()方法

第4部分 实例——JAVA并行异步编程线程池+FutureTask

第5部分 Callable和Future 区别

参考

Callable 和 Future 是比较有趣的一对组合。当我们需要获取线程的执行结果时,就需要用到它们。Callable用于产生结果,Future用于获取结果。

第1部分 Callable

  Callable 是一个接口,它只包含一个call()方法。Callable是一个返回结果并且可能抛出异常的任务。

为了便于理解,我们可以将Callable比作一个Runnable接口,而Callable的call()方法则类似于Runnable的run()方法。

Callable的源码如下:

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

  Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

第2部分 Future

  Future 是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

Future的源码如下:

 boolean cancel(boolean mayInterruptIfRunning)
试图取消对此任务的执行。
V get()
如有必要,等待计算完成,然后获取其结果。
V get(long timeout, TimeUnit unit)
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
boolean isCancelled()
如果在任务正常完成前将其取消,则返回 true。
boolean isDone()
如果任务已完成,则返回 true。

在讲解FutureTask之前,先看看Callable, Future, FutureTask它们之间的关系图,如下:

  Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。在Future规范中包含的隐含意义是,任务的生命周期只能前进,不能后退,就像ExecutorService的生命周期一样。当某个任务完成以后,它就永远停留在“完成”状态上。

  get方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。如果任务已经完成,那么get会立即返回或者抛出一个Exception,如果任务没有完成,那么get将阻塞并直到任务完成。如果任务抛出了异常,那么get将该异常封装为ExecutionException并重新抛出。如果任务被取消,那么get将抛出CancellationException。如果get抛出了ExecutionException,那么可以通过getCause来获得封装的初始异常。

异常抛出:
CancellationException - 如果计算被取消
ExecutionException - 如果计算抛出异常
InterruptedException - 如果当前的线程在等待时被中断
TimeoutException - 如果等待超时

  可以通过很多种方法创建一个Future来描述任务。ExecutorService中的所有submit方法都将返回一个Future,从而将一个Runnable 或 Callable 提交给Executor ,并得到一个Future 用来获得任务的执行结果或者取消任务。 还可以显示地为某个任务指定的Runnable或Callable 实例化一个FutureTask。

说明
(01) RunnableFuture是一个接口,它继承了Runnable和Future这两个接口。RunnableFuture的源码如下:

public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}

(02) FutureTask实现了RunnableFuture接口。所以,也说它实现了Future接口。

第3部分 示例和源码分析

先通过一个示例看看Callable和Future的基本用法,然后再分析示例的实现原理。

 package com.concurrency.TaskExecution_6;

 import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* Callable 和 Future实现线程等待
* @ClassName: CallableFutureTest
* @author Xingle
* @date 2014-9-15 下午3:23:30
*/
public class CallableFutureTest { public static void main(String[] args) throws InterruptedException, ExecutionException{
System.out.println("start main thread ");
ExecutorService exec = Executors.newFixedThreadPool(5); //新建一个Callable 任务,并将其提交到一个ExecutorService. 将返回一个描述任务情况的Future.
Callable<String> call = new Callable<String>() { @Override
public String call() throws Exception {
System.out.println("start new thread ");
Thread.sleep(5000);
System.out.println("end new thread ");
return "返回内容";
}
}; Future<String> task = exec.submit(call);
Thread.sleep(1000);
String retn = task.get();
//关闭线程池
exec.shutdown();
System.out.println(retn+"--end main thread");
} }

执行结果:

3.1 submit()

submit()在java/util/concurrent/AbstractExecutorService.java中实现,它的源码如下:

 public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// 创建一个RunnableFuture对象
RunnableFuture<T> ftask = newTaskFor(task);
// 执行“任务ftask”
execute(ftask);
// 返回“ftask”
return ftask;
}

说明:submit()通过newTaskFor(task)创建了RunnableFuture对象ftask。它的源码如下:

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}

3.2 FutureTask的构造函数

FutureTask的构造函数如下:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
// callable是一个Callable对象
this.callable = callable;
// state记录FutureTask的状态
this.state = NEW; // ensure visibility of callable
}

3.3 FutureTask的run()方法

继续回到submit()的源码中。
在newTaskFor()新建一个ftask对象之后,会通过execute(ftask)执行该任务。此时ftask被当作一个Runnable对象进行执行,最终会调用到它的run()方法;ftask的run()方法在java/util/concurrent/FutureTask.java中实现,源码如下:

 public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
// 将callable对象赋值给c。
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行Callable的call()方法,并保存结果到result中。
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 如果运行成功,则将result保存
if (ran)
set(result);
}
} finally {
runner = null;
// 设置“state状态标记”
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

说明:run()中会执行Callable对象的call()方法,并且最终将结果保存到result中,并通过set(result)将result保存。
      之后调用FutureTask的get()方法,返回的就是通过set(result)保存的值。

第4部分 实例——JAVA并行异步编程线程池+FutureTask

  通过上面的介绍,Callable 和 Future组合使用,可以获取线程的执行结果,实际项目中遇到一个问题,查询数据库的时候,有个执行任务返回的数据非常大,几乎是全部用户群的数据,所以非常耗时,后续处理逻辑要获取到所有返回的数据。这里考虑在查询数据的时候,加上行号,利用线程池多个任务同时查从起始行到结束行的数据,类似分页处理,最后将返回的数据合在一起。

 @Override
public List<BuyerInfoVo> testQuery(final String eticketActId) { int count = grantEticketActDao.testQueryCount(eticketActId);
final int pageSize = 2000;
final int totalPage = count / pageSize+1; long start = System.currentTimeMillis();
// 进行异步任务列表
List<FutureTask<List<BuyerInfoVo>>> futureTasks = new ArrayList<FutureTask<List<BuyerInfoVo>>>();
// 线程池 初始化30个线程
ExecutorService executorService = Executors.newFixedThreadPool(30); Callable<List<BuyerInfoVo>> callable = new Callable<List<BuyerInfoVo>>() {
@Override
public List<BuyerInfoVo> call() throws Exception {
// 执行任务
List<BuyerInfoVo> ls = grantEticketActDao.testQueryBySize(eticketActId,pageSize,totalPage);
return ls;
}
}; List<BuyerInfoVo> reLs = new ArrayList<BuyerInfoVo>();
for (int i = 0; i < totalPage; i++) {
// 创建一个异步任务
FutureTask<List<BuyerInfoVo>> futureTask = new FutureTask<List<BuyerInfoVo>>(
callable);
futureTasks.add(futureTask);
executorService.submit(futureTask);
} for (FutureTask<List<BuyerInfoVo>> futureTask : futureTasks) {
try {
List<BuyerInfoVo> ls = futureTask.get();
if(null!=ls)
reLs.addAll(ls);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
} // 清理线程池
executorService.shutdown(); long end = System.currentTimeMillis(); System.out.println("数据库查询记录总数:"+count);
System.out.println("实际返回数据条数: " + reLs.size());
System.out.println("一共使用时间:"+(end-start)/1000+"s");
if(reLs.size()!=count){
throw new RuntimeException();
}
GrantEticketActServiceImpl.queryId = 0;
return reLs;
}

其中,执行任务查询的代码:

 public List<BuyerInfoVo> testQueryBySize(String eticketActId, int pageSize,
int totalPage) {
int start ;
int end;
synchronized (eticketActId) {
start = (GrantEticketActServiceImpl.queryId)*pageSize+1;
end = (GrantEticketActServiceImpl.queryId+1)*pageSize;
GrantEticketActServiceImpl.queryId++;
}
StringBuffer sb = new StringBuffer();
//查询语句省略
String querysql=" ";
sb.append(" SELECT * FROM (SELECT ROWNUM row_, t.* FROM (");
sb.append(querysql);
sb.append(") t ) WHERE row_ <=");
sb.append(end);
sb.append(" AND row_ >=");
sb.append(start);
String sql = sb.toString(); Object[] args = new Object[]{eticketActId };
List<BuyerInfoVo> list = jdbcTemplate.query(sql, args, new RowMapper<BuyerInfoVo>(){
@Override
public BuyerInfoVo mapRow(ResultSet rs, int i)
throws SQLException {
BuyerInfoVo vo = new BuyerInfoVo();
AutoInjection.Rs2Vo(rs, vo, null);
return vo;
}
}); return list;
}

执行结果说明,原先优化之前,单次执行返回时间需要4min,优化后返回只需15s,效果非常明显,涉及的参数,单次查询条数,以及线程池的配置大小,还有待继续深入。

第5部分 Callable和Future 区别

Callable定义的方法是call,而Runnable定义的方法是run。
Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。
Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。


参考:

1.《java 并发编程实战》 第六章 任务执行

2.java并发编程-Executor框架

3.Java线程(七):Callable和Future

4.JAVA并行异步编程线程池+FutureTask

5.Callable vs Runnable - Runners间的“争论”

项目中代码片段:

/**
* @Description:获取所有有效产品列表
* @return
* @Return:List<SupplyAreaProVo>
* @Author:
* @Date:2016年5月9日 下午12:33:49
*/
private List<SupplyAreaProVo> getTMallProductIndexAll(){
Util.i = 0;
final int total = 10;
List<FutureTask<List<SupplyAreaProVo>>> futureTasks = new ArrayList<FutureTask<List<SupplyAreaProVo>>>();
// 线程池 初始化20个线程
ExecutorService executorService = Executors.newFixedThreadPool(20);
List<SupplyAreaProVo> list=new ArrayList<SupplyAreaProVo>();
for (int index = 0; index < total; index++) {
final int cnt = index;
Callable<List<SupplyAreaProVo>> callable = new Callable<List<SupplyAreaProVo>>() {
@Override
public List<SupplyAreaProVo> call() throws Exception {
// 执行任务
List<SupplyAreaProVo> list= searchTLMallDao.getTMallProductList(total,cnt);
return list;
}
};
// 创建一个异步任务
FutureTask<List<SupplyAreaProVo>> futureTask = new FutureTask<List<SupplyAreaProVo>>(callable);
futureTasks.add(futureTask);
executorService.submit(futureTask);
} for (FutureTask<List<SupplyAreaProVo>> futureTask : futureTasks) {
try { List<SupplyAreaProVo> ls = futureTask.get();
if(null!=ls){
list.addAll(ls);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} }
executorService.shutdown();
return list;
}

并发编程 05—— Callable和Future的更多相关文章

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

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

  2. (转)Java并发编程:Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

  3. Java并发编程:Callable、Future和FutureTask(转)

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

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

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

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

    原文https://www.cnblogs.com/dolphin0520/p/3949310.html Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述 ...

  6. java并发编程--Runnable Callable及Future

    1.Runnable Runnable是个接口,使用很简单: 1. 实现该接口并重写run方法 2. 利用该类的对象创建线程 3. 线程启动时就会自动调用该对象的run方法 通常在开发中结合Execu ...

  7. Java 并发编程:Callable和Future

    项目中经常有些任务需要异步(提交到线程池中)去执行,而主线程往往需要知道异步执行产生的结果,这时我们要怎么做呢?用runnable是无法实现的,我们需要用callable实现. import java ...

  8. Java并发编程:Callable、Future和FutureTask的实现

    启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从Java 1.5开始提供的Callable和Future 下面就分析一下Callable.Future以及FutureTask的 ...

  9. [转载] Java并发编程:Callable、Future和FutureTask

    转载自http://www.cnblogs.com/dolphin0520/p/3949310.html 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Run ...

随机推荐

  1. kafka java代码实现消费者

    public class KafkaConsumer { public static void main(String[] args) { Properties props = new Propert ...

  2. 用"僵尸对象"调试内存管理问题

    Cocoa提供了"僵尸对象"(Zombie Object)这个功能.启用这项调试功能之后,运行时系统会把所有已经回收的实例转化成特殊的"僵尸对象",而不会真正回 ...

  3. SecureCRT使用小技巧

    SecureCRT使用小技巧  作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 欢迎加入:高级运维工程师之路 598432640 以前觉得xshell链接工具挺好的,看起来又好耍,图 ...

  4. 【转】ACM/ICPC生涯总结暨退役宣言—alpc55

    转自:http://hi.baidu.com/accplaystation/item/ca4c2ec565fa0b7fced4f811 ACM/ICPC生涯总结暨退役宣言—alpc55 前言 早就该写 ...

  5. 英文不好也能快速"记忆" API

    英文不好不要紧,把API函数导入打字练习类软件,即是练习打字速度,提高编程效率:也能短时间记忆API. 坚持每天打一遍,约2小时,连续打两周,会对API有很好的记忆,此方法是结合英文学习方法!以下是W ...

  6. HttpClient_001_初步实现项目01的servlet,与项目02的servlet,之间数据访问

    HttpClient_001_初步实现项目01的servlet,与项目02的servlet,之间数据访问 代码下载地址: http://download.csdn.net/detail/poiuy19 ...

  7. paper 106:图像增强方面的介绍

    图像增强是从像素到像素的操作,是以预定的方式改变图像的灰度直方图.有时又称为对比度增强,灰度变换.点运算不可能改变图像内的空间关系,输出像素的灰度值由输入像素的值决定.其作用: 对比度增强:扩展感兴趣 ...

  8. jvm基础笔记

    名词解释: 三类参数:标准参数(可能不会变的,java -help列出来的就是这类的),X参数(非标准化参数),XX参数(扩展参数). 所有XX 参数都以-XX开始,但后面出现的+-就不同了.+代表激 ...

  9. sysobjects中字段的含义

    --列名 数据类型 描述 name sysname --对象名. Id int --对象标识号. xtype ) --对象类型.可以是下列对象类型中的一种: C = CHECK --约束 D = -- ...

  10. php中curl模拟浏览器来传输数据

    cURL可以使用URL的语法模拟浏览器来传输数据, 因为它是模拟浏览器,因此它同样支持多种协议,FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE 以 ...