出处:https://blog.csdn.net/kity9420/article/details/80740466

前言
  经常会遇到一些性能问题,比如调用某个接口,可能要循环调用100次,并且需要拿到每一次调用的返回结果,通常我们都是放在for循环中一次次的串行调用,这种方式可想而知道有多慢,那怎么解决这个问题呢?

多线程
  为了解决以上问题,我使用的方式是多线程。多线程常规的有两种实现方式,即继承Tread类,实现Runnable接口,但是这两种实现方式,有一个共同的问题,就是没有返回值,对于我们来说,获得每个线程的返回值,是个很困难的问题,因此不能用Tread类或Runnable接口,我用的是Callable和ThreadPoolExecutor,Callable的process方法可以允许有返回值,ThreadPoolExecutor的invokeAll或submit方法可以拿到线程的执行结果

案例
  假设需要给100个用户发送邮件,并需要每个用户的返回结果,先看下代码结构

CallableTemplate.java

package com.gdut.thread.multiThread;

import java.util.concurrent.Callable;

/**
* 多线程模板类
* @author yang.han
*
* @param <V>
*/
public abstract class CallableTemplate<V> implements Callable<V>{ /**
* 前置处理,子类可以Override该方法
*/
public void beforeProcess() {
System.out.println("before process");
} /**
* 处理业务逻辑的方法,需要子类去Override
* @param <V>
* @return
*/
public abstract V process(); /**
* 后置处理,子类可以Override该方法
*/
public void afterProcess() {
System.out.println("after process");
} @Override
public V call() throws Exception {
beforeProcess();
V result = process();
afterProcess();
return result;
} }

  CallableTemplate类实现了Callable接口,并实现了process方法,该类是一个抽象类,接收任意返回值的类型,beforeProcess方法为前置处理,afterProcess的后置处理,process为具体的业务逻辑抽象方法,该方法在子类中实现

IConcurrentThreadPool.java

package com.gdut.thread.multiThread;

import java.util.List;
import java.util.concurrent.ExecutionException; public interface IConcurrentThreadPool { /**
* 初始化线程池
*/
void initConcurrentThreadPool(); /**
* 提交单个任务
* @param <V>
* @param task
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
<V> V submit(CallableTemplate<V> task) throws InterruptedException, ExecutionException; /**
* 提交多个任务
* @param <V>
* @param tasks
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
<V> List<V> invokeAll(List<? extends CallableTemplate<V>> tasks) throws InterruptedException, ExecutionException;
}

  IConcurrentThreadPool是多线程接口类,声名了三个方法,initConcurrentThreadPool:初始化线程池,submit:提交单个任务的线程,并有返回值,invokeAll:提交多个任务的线程,并有返回值

ConcurrentThreadPool.java

package com.gdut.thread.multiThread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class ConcurrentThreadPool implements IConcurrentThreadPool{ private ThreadPoolExecutor threadPoolExecutor;
// 核心线程数
private int corePoolSize = 10;
// 最大线程数
private int maximumPoolSize = 20;
// 超时时间30秒
private long keepAliveTime = 30; @Override
public void initConcurrentThreadPool() {
threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>()
);
} @Override
public <V> V submit(CallableTemplate<V> task) throws InterruptedException, ExecutionException {
Future<V> result = threadPoolExecutor.submit(task);
return result.get();
} @Override
public <V> List<V> invokeAll(List<? extends CallableTemplate<V>> tasks) throws InterruptedException, ExecutionException {
List<Future<V>> tasksResult = threadPoolExecutor.invokeAll(tasks);
List<V> resultList = new ArrayList<V>(); for(Future<V> future : tasksResult) {
resultList.add(future.get());
}
return resultList;
} }

  ConcurrentThreadPool是创建线程池的实现类,用到了ThreadPoolExecutor线程池类及这个类的invokeAll方法和submit方法,这两个方法的返回值,都可以通过Future类的get方法获得

ICallableTaskFrameWork.java

package com.gdut.thread.multiThread;

import java.util.List;
import java.util.concurrent.ExecutionException; public interface ICallableTaskFrameWork {
<V> List<V> submitsAll(List<? extends CallableTemplate<V>> tasks)
throws InterruptedException, ExecutionException;
}

  ICallableTaskFrameWork是定义的线程任务框架接口,所有的多线程调用,都通过该接口发起

CallableTaskFrameWork.java

package com.gdut.thread.multiThread;

import java.util.List;
import java.util.concurrent.ExecutionException; public class CallableTaskFrameWork implements ICallableTaskFrameWork{ private IConcurrentThreadPool concurrentThreadPool = new ConcurrentThreadPool(); @Override
public <V> List<V> submitsAll(List<? extends CallableTemplate<V>> tasks)
throws InterruptedException, ExecutionException {
concurrentThreadPool.initConcurrentThreadPool();
return concurrentThreadPool.invokeAll(tasks);
} }

  CallableTaskFrameWork是ICallableTaskFrameWork 的实现类,在submitsAll实现方法中,通过调用线程池对象IConcurrentThreadPool接口的invokeAll方法来发起多线程的调用,这里注意一个,在submitAll实现方法中,我手动的调用了初始化线程池的方法concurrentThreadPool.initConcurrentThreadPool(),在真实的项目上,应该在应用启动的时候就调用该方法来初始化线程池

测试类代码 
SendMessageService.java,假设这是一个发送邮件信息的服务类

package com.gdut.thread.multiThread;

public class SendMessageService {
public void sendMessage(String email,String content){
System.out.println("发送邮件。。。");
}
}

SendMessageHander.java,多线程发送邮件的处理类

package com.gdut.thread.multiThread;

import java.util.HashMap;
import java.util.Map; public class SendMessageHander extends CallableTemplate<Map<String, String>>{ private String email;
private String content;
public SendMessageHander(String email,String content) {
this.email = email;
this.content = content;
} @Override
public Map<String, String> process() {
SendMessageService sendMessageService = new SendMessageService();
sendMessageService.sendMessage(email, content);
Map<String, String> map = new HashMap<String, String>();
map.put(email, content);
return map;
} }

  这个类继承了上面的CallableTemplate,我们要的返回值是Map,因此泛型类型是Map,在类中还重写了process方法,在方法中调用发送邮件的业务逻辑接口SendMessageService.sendMessage,并将返回结果组装成Map返回,这里我就简单处理了,将邮件地址及内容放在Map中直接返回了;另外还要注意这个类有个有参构造器,通过构建器可以接收需要传递进来的参数

SendMessageTest.java,测试类

package com.gdut.thread.multiThread;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException; public class SendMessageTest { public static void main(String[] args) throws InterruptedException, ExecutionException {
ICallableTaskFrameWork callableTaskFrameWork = new CallableTaskFrameWork(); List<CallableTemplate<Map<String, String>>> tasks = new ArrayList<CallableTemplate<Map<String, String>>>(); SendMessageHander sendMessageHander = null; // 将需要发送邮件的邮件地址及内容组装好,放在一个集合中
for (int i = 0; i < 1000; i++) {
sendMessageHander = new SendMessageHander("email" + i, "content" + i);
tasks.add(sendMessageHander);
} //通过多线程一次性发起邮件,并拿到返回结果集
List<Map<String, String>> results = callableTaskFrameWork.submitsAll(tasks); // 解析返回结果集
for (Map<String, String> map : results) {
for (Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
}
} }

运行结果

附录:还可以看这边文章: java并发异步编程 原来十个接口的活现在只需要一个接口就搞定!

Callable+ThreadPoolExecutor实现多线程并发并获得返回值(转)的更多相关文章

  1. Java多线程-新特性-有返回值的线程

    在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写.或者干脆绕过这道坎,走别的路了. 现在Java终于有可返回值的任务(也可以叫做线程)了. 可返回值的任务必须实现 ...

  2. Java 多线程 | 并发知识问答总结

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  3. Java 多线程 并发编程

    一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...

  4. python多进程并发和多线程并发和协程

    为什么需要并发编程? 如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这样会浪费系统资源,浪费时间 1.Python的并发编程分为多进程并发和多线程并发 多进程并发:运行多个独立的 ...

  5. Java接口多线程并发测试 (二)

    原文地址http://www.cnblogs.com/yezhenhan/archive/2012/01/09/2317636.html 这是一篇很不错的文章,感谢原博主的分享! JAVA多线程实现和 ...

  6. Java中多线程并发体系知识点汇总

    一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...

  7. Java 多线程 并发编程 (转)

    一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进程的地址空间是互相隔离的:进程拥有各种 ...

  8. Java多线程并发技术

    Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...

  9. 如何实现有返回值的多线程 JAVA多线程实现的三种方式

    可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口.执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable ...

随机推荐

  1. Color a Tree

    题目链接:Click here Solution: 看起来不太能dp,则考虑树上贪心 题目要求一个点必须先染父亲才能染自己,就给了我们启示,贪心的合并点 我们定义一个点的权重为这个点的价值和/点数,然 ...

  2. luoguP1041 传染病控制 x

    P1041 传染病控制 题目背景 近来,一种新的传染病肆虐全球.蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延.不幸的是,由于人们尚未完全认识这种传染 ...

  3. Springboot 解决跨域请求

    Cors处理 跨域请求 细粒度 直接在controller层上 添加@CrossOrigin注解 @PostMapping("/") @CrossOrigin(value = &q ...

  4. Celery分布式异步任务框架

    一.什么是Celery Celery是一个简单.灵活且可靠的,处理大量消息的分布式系统.专注于实时处理的异步任务队列,同时也支持定时任务 二.Celery架构 1.Celery的架构由三部分组成: 消 ...

  5. 论文阅读:Camdoop: Exploiting In-network Aggregation for Big Data Applications

    摘要: 大公司与中小型企业每天都在批处理作业和实时应用程序中处理大量数据,这会产生大量的网络流量,而使用传统的的网络基础架构则很难支持.为了解决这个问题已经提出了几种新颖的网络拓扑,旨在增加企业集群中 ...

  6. SSM框架搭建,以及mybatis学习

    前两天在研究SSM框架,然后看到一篇博文,写的很清晰,照着实现了一下,这里就不重复写了,把博文地址留一下 http://blog.csdn.net/zhshulin/article/details/3 ...

  7. gdb调试时忽略SIGPIPE 等信号

    GDB调试网络程序时,会遇到SIGPIPE信息,默认GDB会把程序停下来,即使程序使用signal(SIGPIPE, SIG_IGN);来忽略信号.用handle命令设置一下缺省的signal的处理行 ...

  8. spring的AOP基本原理

    一.什么是AOP AOP(Aspect Oriented Programming),意思是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP基于IoC基础,是对OOP ...

  9. laravel中事件的监听和订阅

    一.前言 更新员工部门主管的时候,需要重新更新一下缓存,这个会比较耗时.所以计划放到队列中来执行.后来想了想,其实用一下事件监听也能实现.人家都说好,然是我也没感觉到有什么好的. 二.正文 1. 在p ...

  10. IJCAI 2019 Analysis

    IJCAI 2019 Analysis 检索不到论文的关键词:retrofitting word embedding Getting in Shape: Word Embedding SubSpace ...