您的位置:首页 > 编程语言 > Java开发

多线程之Future和Callable【高性能应用场景java.util.concurrent】

2017-04-09 17:39 585 查看
业务场景

如查一个数据集合,第一页至第一百页,返回总页数的总结集,然后导出。

一次需要limit 0 10000,这样,一个SQL查询出非常慢。

但用100个线程,一个线程只查limit0 10 就非常快了,

利用多线程的特性,返回多个集合,在顺序合并成总集合。

下面是concurrent.Future 例子

concurrent.Callable

package com.test.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
*  Future<List<User>>
*
*  应用场景 :1.多线程,每个线程平均任务耗时一样
*          2.需要顺序返回的
*
*  顺序返回,如多个分页,但第一页需要先返回
*
* @author 汪兴安
*
*/
public class ThreadTest {

/**
* 定义线程池
*/
public static ExecutorService executos = Executors.newFixedThreadPool(5);

public static void main(String[] args) {
/**
* 定义集合装结果集
*/
List<User> totalList = new ArrayList<User>();

/**
* 定义 Future泛型的集合对象,装多线程的,返回信息
*/
List<Future<List<User>>> list = new ArrayList<Future<List<User>>>();
try {
int pageIndex = 0;
int maxPage = 6;
for (pageIndex = 0; pageIndex < maxPage; pageIndex++) {
/**
* executos.submit 返回线程未来结果
*/
Future<List<User>> future = executos.submit(new DemoThread(pageIndex));
list.add(future);
}

for (Future<List<User>> dataFuture : list) {
//// 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
totalList.addAll(dataFuture.get());  //dataFuture.get()  这里会阻塞   有顺序的哦
}

//得到分页后结果总共集合
for (int i = 0; i < totalList.size(); i++) {
System.out.println(totalList.get(i).getName());
}

} catch (Exception e) {
e.printStackTrace();
}

executos.shutdownNow();
}

/**
*  查询第1页
查询第3页
查询第5页
查询第2页
查询第4页
查询第0页
-----结果会顺序返回,不用你操心顺序问题啦
anan0
anan1
anan2
anan3
anan4
anan5

*/
}


package com.test.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

/**
* Callable 支持有返回值的多线程
* @author 汪兴安
*
*/
public class DemoThread implements Callable<List<User>> {
private int pageIndex;

/**
* 通过构造器,初始化
* @param pageIndex
*/
public DemoThread(int pageIndex) {
this.pageIndex = pageIndex;
}

@Override
public List<User> call() throws Exception {
//模拟每个线程执行耗时不一样
if(pageIndex%2==0)
{
Thread.sleep(5000);
}else
{
Thread.sleep(2000);
}

System.out.println("查询第" + pageIndex + "页");
List<User> list = new ArrayList<User>();

/**
* 模拟查询的数据对象
*/
User user=new User();
user.setAge(""+pageIndex);
user.setName("anan"+pageIndex);

list.add(user);
return list;
}

}


===================================华丽分割线=================================================================================

package com.test.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutionException;

/**
*
* @author 汪兴安
* 应用场景 :1.每个线程耗时差别大,谁先完成,谁返回
*           对顺序没有要求。
*
*
*
* CompletionService 的性能更高。
* 考虑如下场景:多线程下载,结果用Future返回。
* 第一个文件特别大,后面的文件很小。
* 用方法1,能很快知道已经下载完文件的结果(不是第一个);
*
*/
public class CallableAndFuture {

public static void main(String[] args) {
List<User> totalList = new ArrayList<User>();

ExecutorService threadPool = Executors.newCachedThreadPool();
CompletionService<List<User>> cs = new ExecutorCompletionService<List<User>>(threadPool);
for(int i = 0; i < 6; i++) {
cs.submit(new DemoThread(i));
}
// 可能做一些事情
for(int i = 0; i < 6; i++) {
try {
totalList.addAll(cs.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}

//得到分页后结果总共集合
for (int i = 0; i < totalList.size(); i++) {
System.out.println(totalList.get(i).getName());
}
}
/**
* 查询第3页
查询第1页
查询第5页
查询第2页
查询第0页
查询第4页
anan5
anan1
anan3
anan2
anan0
anan4
*/

}


总结:两种方式,应用场景不一样

Future<List<User>> future = executos.submit(new DemoThread(pageIndex)); 这种可以保证返回的顺序

CompletionService<List<User>> cs = new ExecutorCompletionService<List<User>>(threadPool);,返回不保证顺序,谁先执行完,谁先返回。

效率上CompletionService更高,实际项目需要根据具体业务需求选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: