前言:最近项目中与融360项目中接口对接,有反馈接口(也就是我们接收到请求,需要立即响应,并且还要有一个接口推送给他们其他计算结果),推送过程耗时、或者说两个接口不能是同时返回,有先后顺序。

这时我想到了把自己Controller立即返回接受成功,中间添加一个新的线程去做其他耗时的操作(线程池配置和参数测试讲解请阅读第5步)。

1、Controller代码如下:

@Autowired
private CallThreadDemo worker;
@RequestMapping("/bandBankConfirm2")
public void bandBankConfirm2(String jsonString) {
System.out.println("controller开始--------------");
    //这里是需要调用第三方的接口传入参数
String method = "is.api.v3.order.bindcardfeedback";
Map<String, Object> map = new HashMap<>();
map.put("order_no", "254986512848973");
map.put("bind_status", 1);
map.put("reason", "");
    //这里开始调用线程池的方法
worker.callRong360(method, map);
System.out.println("controller end --------------");
    //renderJson这个方法是jfinal的,可以理解为@ReponseBody 需要return的操作了
    renderJson("接口调用完毕");   
}
2、调用线程池的方法 CallThreadDemo 类代码:
@Component
public class CallThreadDemo {

   //这里是Spring.xml中配置的bean名称
@Autowired
private ThreadPoolTaskExecutor executor;
@Autowired
private ServiceTest serviceTest; public void callRong360(final String method, final Map<String, Object> map) {
//这个类是我封装的抽象类,里面有一个公共方法,具体代码下面有
ServiceParent callRong360Method = new ServiceParent() {
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程开始-------------");
          //这里调用第三方公用接口
JSONObject result = callBandBankMethod(method, map);           //这里调用service方法,实现自己的业务逻辑
serviceTest.insertUser("111", "222222");
System.out.println(result);
System.out.println("线程结束-------------"); }
};
     //这里线程池方法调用一个线程继承类或者实现Runable接口的类
executor.execute(callRong360Method); System.out.println("当前活动线程数:"+ executor.getActiveCount());
System.out.println("核心线程数:"+ executor.getCorePoolSize());
System.out.println("总线程数:"+ executor.getPoolSize());
System.out.println("最大线程池数量"+executor.getMaxPoolSize());
System.out.println("线程处理队列长度"+executor.getThreadPoolExecutor().getQueue().size());
}

3、封装的抽象类代码如下:

public abstract class ServiceParent implements Runnable {

    public JSONObject callBandBankMethod(String method, Map<String, Object> map) {
//这个方法是调用三方的接口,公用部分
// //输入 参数如下 :
// Map<String, Object> map = new HashMap<>();
// map.put("order_no", "254986512848973");
// map.put("bind_status", 1);
// map.put("reason", "");
// net.sf.json.JSONObject ret = callRong360Method.callBandBankMethod("is.api.v3.order.bindcardfeedback", map);
//
// // 异常情况输出参数 为 null:
OpenapiClient openapiClient = new OpenapiClient();
openapiClient.setMethod(method);
for (Map.Entry<String, Object> entry : map.entrySet()) {
openapiClient.setField(entry.getKey(), String.valueOf(entry.getValue()));
}
net.sf.json.JSONObject ret = null;
try {
ret = openapiClient.execute();
} catch (Exception e) {
e.printStackTrace();
System.out.println("调用反馈接口异常---->" + e.getMessage());
}
return ret;
}
  //这里定义为抽象方法,创建匿名内部类或者继承类必须实现
public abstract void run();

4、ServiceTest接口方法如下:

@Service
public class ServiceTest { @Autowired
private BankMapper bankMapper; @Transactional(rollbackFor = {Exception.class})
public void insertUser(String s, String s1) {
bankMapper.insertUser(s, s1); //这里创建异常,测试事务。已测试,事务生效
int i = 1 / 0;
}
}

以上四步,成功在调用Controller方法立即返回结果,也实现了另外的一个线程去调用三方接口返回信息,并且实现了自己的业务逻辑。

5、接着我们继续说明一下线程池对应配置和参数说明,这里我们通过测试来说明参数的作用(corePoolSize 核心线程数量,maxPoolSize 最大线程数,queueCapacity 处理队列)

  threadpool.corePoolSize=5
  threadpool.keepAliveSeconds=200
  threadpool.maxPoolSize=10
  threadpool.queueCapacity=2
  <!-- 线程池 -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="${threadpool.corePoolSize}" />
<property name="keepAliveSeconds" value="${threadpool.keepAliveSeconds}" />
<property name="maxPoolSize" value="${threadpool.maxPoolSize}" />
<property name="queueCapacity" value="${threadpool.queueCapacity}" />
</bean>
<!--请在bean xsd中配置task-->
<task:annotation-driven executor="taskExecutor" />

下面贴出测试输出的日志信息:controller开始--------------

当前活动线程数:1
核心线程数:5
总线程数:1
最大线程池数量10
线程处理队列长度0
controller end -------------- controller开始--------------
当前活动线程数:2
核心线程数:5
总线程数:2
最大线程池数量10
线程处理队列长度0
controller end -------------- controller开始--------------
当前活动线程数:3
核心线程数:5
总线程数:3
最大线程池数量10
线程处理队列长度0
controller end -------------- controller开始--------------
当前活动线程数:4
核心线程数:5
总线程数:4
最大线程池数量10
线程处理队列长度0
controller end -------------- controller开始--------------
当前活动线程数:5
核心线程数:5
总线程数:5
最大线程池数量10
线程处理队列长度0
controller end -------------- controller开始--------------
当前活动线程数:5---------------》因为放入了队列中,此处活动线程数还是5
核心线程数:5
总线程数:5
最大线程池数量10
线程处理队列长度1 -------------》这里达到了最大的核心线程数量 corePoolSize=5,开始放入处理队列中
queueCapacity=2
controller end --------------
controller开始--------------
当前活动线程数:5
核心线程数:5
总线程数:5
最大线程池数量10
线程处理队列长度2 ---------------》继续放入队列中,达到了最大队列数量2
controller end -------------- controller开始--------------
当前活动线程数:6-----------------》这里因为达到了最大队列数量,所以继续创建线程去执行,一直到最后到最大线程数量
核心线程数:5
总线程数:6
最大线程池数量10
线程处理队列长度2
controller end -------------- controller开始--------------
当前活动线程数:7
核心线程数:5
总线程数:7
最大线程池数量10
线程处理队列长度2
controller end -------------- controller开始--------------
当前活动线程数:8
核心线程数:5
总线程数:8
最大线程池数量10
线程处理队列长度2
controller end -------------- controller开始--------------
当前活动线程数:9
核心线程数:5
总线程数:9
最大线程池数量10
线程处理队列长度2
controller end -------------- controller开始--------------
当前活动线程数:10 --------------》这里活动线程数量达到了最大线程池数量
核心线程数:5
总线程数:10
最大线程池数量10
线程处理队列长度2
controller end -------------- controller开始-------------- 2018-07-01 20:23:19 -----------------》这里继续调用,因为最大线程池数量和队列中都已经到了最大值,抛出了异常
[] [] [WARN]-[Thread: qtp1276504061-60]-[org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException()]: Handler execution resulted in exception
org.springframework.core.task.TaskRejectedException: Executor [java.util.concurrent.ThreadPoolExecutor@17d95b77[Running, pool size = 10, active threads = 10, queued tasks = 2, completed tasks = 0]] did not accept task: com.fastx.cooperate.rong360.rong.service.CallThreadDemo$1@5fa6bf1
at org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.execute(ThreadPoolTaskExecutor.java:245)
at com.fastx.cooperate.rong360.rong.service.CallThreadDemo.callRong360(CallThreadDemo.java:43)

这样,通过输出的日志,我们可以很容易的理解了各个参数的作用。

corePoolSize: 线程池维护线程的最少数量 
keepAliveSeconds 线程池维护线程所允许的空闲时间 
maxPoolSize 线程池维护线程的最大数量 
queueCapacity 线程池所使用的缓冲队列

 

ThredPoolTaskExcutor的处理流程:

  当池子大小小于corePoolSize,就新建线程,并处理请求

  当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去workQueue中取任务并处理

  当workQueue放不下任务时,就新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize,就用RejectedExecutionHandler来做拒绝处理

  当池子的线程数大于corePoolSize时,多余的线程会等待keepAliveTime长时间,如果无请求可处理就自行销毁

  其会优先创建  CorePoolSiz 线程, 当继续增加线程时,先放入Queue中,当 CorePoolSiz  和 Queue 都满的时候,就增加创建新线程,当线程达到MaxPoolSize的时候,就会抛出错 误 org.springframework.core.task.TaskRejectedException

  另外MaxPoolSize的设定如果比系统支持的线程数还要大时,会抛出java.lang.OutOfMemoryError: unable to create new native thread 异常。

记录ThreadPoolTaskExecutor线程池的在项目中的实际应用,讲解一下线程池的配置和参数理解。的更多相关文章

  1. .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块

    .Net Core ORM选择之路,哪个才适合你   因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...

  2. android 项目中使用到的网络请求框架以及怎样配置好接口URL

    我们在做项目中一定少不了网络请求,如今非常多公司的网络请求这块好多都是使用一些比較好的开源框架,我项目中使用的是volley,如今讲讲一些volley主要的使用,假设想要具体的了解就要去看它的源代码了 ...

  3. 【开发记录】如何在B/S项目中使用中国天气的实时天气功能

    好久没有更新我的博客了,正好手头有一个比较合适的项目经验可以分享出来,就是这个如何使用中国天气的天气预报功能,也正好做个项目经验记录. 功能需求 这个功能需求比较简单,就是想在网页端显示实时天气数据. ...

  4. Maven项目中通过profile定义使不同环境使用不同配置信息

    profile可以让我们定义一系列的配置信息,然后指定其激活条件.这样我们就可以定义多个profile,然后每个profile对应不同的激活条件和配置信息,从而达到不同环境使用不同配置信息的效果.比如 ...

  5. Druid使用起步—在javaWeb项目中配置监控 连接池

    当我们在javaWEB项目中使用到druid来作为我们的连接池的时候,一定不会忘了添加监控功能.下面我们就来看一下,在一个简单的web项目中(尚未使用任何框架)我们是如果来配置我们的web.xml来完 ...

  6. 项目中oracle存储过程记录——经常使用语法备忘

    项目中oracle存储过程记录--经常使用语法备忘 项目中须要写一个oracle存储过程,需求是收集一个复杂查询的内容(涉及到多张表),然后把符合条件的记录插入到目标表中.当中原表之中的一个的日期字段 ...

  7. 记录第一次在egret项目中使用Puremvc

    这几天跟着另一个前端在做一个小游戏,使用的是egret引擎和puremvc框架,这对于我来说还是个比较大的突破吧,特此记录下. 因为在此项目中真是的用到了mvc及面向对象编程,值得学习 记录第一次在e ...

  8. C#~异步编程在项目中的使用

    一些闲话 对异步编程没有了解的同学可以看我的这篇文章<C#~异步编程>,今天主要说一下,在项目中怎么就用到了异步编程!在进行WEB开发时,异步这块我们用的并不多,但当你的项目做到一定规模时 ...

  9. 在.NET项目中使用PostSharp,使用CacheManager实现多种缓存框架的处理

    在前面几篇随笔中,介绍了PostSharp的使用,以及整合MemoryCache,<在.NET项目中使用PostSharp,实现AOP面向切面编程处理>.<在.NET项目中使用Pos ...

随机推荐

  1. VSCode插件开发全攻略(一)概览

    文章索引 VSCode插件开发全攻略(一)概览 VSCode插件开发全攻略(二)HelloWord VSCode插件开发全攻略(三)package.json详解 VSCode插件开发全攻略(四)命令. ...

  2. PMS权限管理和鉴权过程

    一.权限的管理基础知识 1.系统的权限机制分为:权限解析.权限分配.鉴权.动态添加权限 2.PermissionInfo :  PackageParser.Permission中包含一个对应的Perm ...

  3. 第71节:Java中HTTP和Servlet

    第71节:Java中HTTP和Servlet 前言 哭着也要看完!!!字数: 学习xml和TomCat 会写xml,看懂xml 解析对象 SAXReader reader = new SAXReade ...

  4. 全栈开发工程师微信小程序 - 上

    全栈开发工程师微信小程序-上 实现swiper组件 swiper 滑块视图容器. indicator-dots 是否显示面板指示点 false indicator-color 指示点颜色 indica ...

  5. IDEA项目上传到github

    IDEA项目上传到github 保证下载了Git插件 往后余生,唯独有你 简书作者:达叔小生 90后帅气小伙,良好的开发习惯:独立思考的能力:主动并且善于沟通 简书博客: https://www.ji ...

  6. redux源码学习笔记 - createStore

    本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1. 在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱.r ...

  7. Spring Boot 主类及目录结构介绍

    Spring Boot 与传统项目最大的区别是,传统项目都是打成 WAR 包部署到服务器上面,需要额外的 Servlet 容器, 而 Spring Boot 则可以直接打成 jar 包,并内置集成了 ...

  8. Java异常处理 10 个最佳实践

    异常处理是Java 开发中的一个重要部分.它是关乎每个应用的一个非功能性需求,是为了处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch 和 ...

  9. 深度学习Dubbo系列(入门开篇)

    此文档为系列学习文档 这系列文档详细讲解了dubbo的使用,基本涵盖dubbo的所有功能特性.在接下来的文章里会详细介绍. 如果你正依赖dubbo作为你业务工程的RPC通信框架,这里可以作为你的参考手 ...

  10. word标题文字居中浅谈

    在Word排版时,要将标题在文档居中,是有区别的,如下图 在回车键后,在选择标题居中,我们常认为标题就是在整个文档居中了,但是实际上只是在回车键到右边区域居中而已,如上图红色方块居中. 只有在标题文字 ...