JUC回顾之-ThreadPoolExecutor的原理和使用
Spring中的ThreadPoolTaskExecutor是借助于JDK并发包中的java.util.concurrent.ThreadPoolExecutor来实现的。基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中。
ThreadPoolExecutor的构建参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1. 参数解释
corePoolSize: 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
maximumPoolSize: 线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
unit: 线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue: 线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
解释:
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
- LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
- SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
- priorityBlockingQuene:具有优先级的无界阻塞队列;
handler: 线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy()。
解释:
线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
2.ThreadPoolExecutor内运转机制:
具体流程如下:当一个任务通过execute(Runnable)方法欲添加到线程池时:
- 当池子大小小于corePoolSize就新建线程,并且处理请求。
- 当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理。
- 当workQueue放不下新任务时,新建线程入池,并处理请求,如果池子的大小撑到maximumPoolSize就用RejectedExecutionHandler拒绝处理。
- 另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁。
3.Exectors
Exectors工厂类提供了线程池的初始化接口,主要有如下几种:
newFixedThreadPool

初始化一个指定线程数的线程池,其中corePoolSize == maximumPoolSize,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。
注意:由于LinkedBlockingQuene队列的大小是整型的最大值,队列是无界队列,所以不建议直接使用,newFixedThreadPool可能导致内存爆满的问题;
newCachedThreadPool

1、初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
2、和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
注意:使用该线程池时,一定要注意控制并发的任务数,因为最大的线程数可以达到整型的最大值,线程数是无限的,当并发的任务很多时候,会同时创建大量的线程,会导致严重的性能问题。
newSingleThreadExecutor

初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列。
注意:由于LinkedBlockingQuene队列的大小是整型的最大值,队列是无界队列,所以不建议直接使用,newSingleThreadExecutor可能导致内存爆满的问题;
newScheduledThreadPool

初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。

3.spingMVC中使用线程池,用做批处理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:oscache="http://www.springmodules.org/schema/oscache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springmodules.org/schema/oscache http://www.springmodules.org/schema/cache/springmodules-oscache.xsd">
<!-- spring 加载资源文件的配置 -->
<context:property-placeholder
location="classpath:conf/custom/env/config.properties"
ignore-unresolvable="true" /> <!-- spring 线程池配置 start -->
<!-- insurance线程池 -->
<bean id="insuranceTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="${insurance.taskexecutor.corePoolSize}" />
<property name="maxPoolSize" value="${insurance.taskexecutor.maxPoolSize}" />
<property name="keepAliveSeconds" value="${insurance.taskexecutor.keepAliveSeconds}" />
<property name="queueCapacity" value="${insurance.taskexecutor.queueCapacity}" />
</bean> </beans>
config里面对于线程池的配置:
#insurance task setting
insurance.taskexecutor.corePoolSize=2
insurance.taskexecutor.maxPoolSize=2
insurance.taskexecutor.keepAliveSeconds=30
insurance.taskexecutor.queueCapacity=1000
Controller 层部分代码:
/**
*
* sendInsuranceBatch
*
* @Title: sendInsuranceBatch
* @Description: TODO 投保批处理
* @param pojo
* @param errors
* @param request
* @return
*/
@RequestMapping("/ihotel_insurance_deliver")
@ResponseBody
public String sendInsuranceBatch(
@ModelAttribute("pojo") IhotelBatchPojo pojo, Errors errors,
HttpServletRequest request) {
String remoteIp = WebUtils.getRemoteIpAddress(request);
String serverIP = IPUtil.getServerIp();
BaseResultInfo baseResultInfo = null;
// 验证入参是否正确
try {
// 1,校验参数
pojo.validate(pojo, errors);
if (errors.hasErrors()) {
baseResultInfo = new BaseResultInfo(
ResultType.PARAMETER_VERIFY_FAILUE);
baseResultInfo.appendRetdesc("#" + getErrMsg(errors));
return JsonUtil.BeanToJson(baseResultInfo);
}
ihotelInsuranceBatchService.processIhotelInsuredItem(pojo, remoteIp);
baseResultInfo = new BaseResultInfo();
baseResultInfo.setRetcode(0);
baseResultInfo.setRetdesc("success");
baseResultInfo.setServerIP(serverIP);
return JSON.toJSONString(baseResultInfo);
} catch (Exception e) {
IHotelLoggerUtil.error("[国际酒店保险投保批处理异常]", e);
throw e;
}
}
Service层部分代码:
@Service("ihotelInsuranceBatchService")
public class IhotelInsuranceBatchService {
// 保险线程池
@Resource
private ThreadPoolTaskExecutor insuranceTaskExecutor;
public void processIhotelInsuredItem(IhotelBatchPojo pojo, String remoteIp) {
// 根据入参条件取出要投保批处理的数据
List<InsuredHotelItem> isuredHotelList = this.findListByCond(pojo);
for (InsuredHotelItem ihotelItem : isuredHotelList) {
//将任务放入线程池
insuranceTaskExecutor.execute(new IhotelInsureDeliverTask(
ihotelItem, this, remoteIp));
}
}
}
线程类
/**
* @Title: IhotelInsureDeliverTask.java
* @Package com.elong.ihotel.service.insurance
* @Description: TODO
* Copyright: Copyright (c) 2014
* Email: songbin0819@163.com
*
* @author user
* @date 2014年11月7日 下午4:04:02
* @version V1.0
*/ package com.elong.ihotel.service.insurance; import com.elong.ihotel.model.insurance.InsuredHotelItem;
import com.elong.ihotel.util.IHotelLoggerUtil; /**
* IhotelInsureDeliverTask
*
* @Title: IhotelInsureDeliverTask
* @Description: TODO 保险投保批处理线程类
* @author Peng.Li
* @date 2014年11月7日 下午4:04:02
*
*/ public class IhotelInsureDeliverTask implements Runnable {
/**
* 需要批处理的保险中间表实体
*/
private InsuredHotelItem ihotelItem; private IhotelInsuranceBatchService ihotelInsuranceBatchService;
/**
* ip
*/
private String remoteIp; /**
* Constructor for IhotelInsureDeliverTask.
* <p>
* Title:
* </p>
* <p>
* Description:
* </p>
*/
public IhotelInsureDeliverTask() {
} /**
* Constructor for IhotelInsureDeliverTask.
* <p>
* Title:
* </p>
* <p>
* Description:
* </p>
*
* @param ihotelItem
* @param ihotelInsuranceBatchService
* @param remoteIp
*/
public IhotelInsureDeliverTask(InsuredHotelItem ihotelItem,
IhotelInsuranceBatchService ihotelInsuranceBatchService, String remoteIp) {
this.ihotelItem = ihotelItem;
this.ihotelInsuranceBatchService = ihotelInsuranceBatchService;
this.remoteIp = remoteIp;
} /**
*
* override run
* <p>
* Title: run
* </p>
* <p>
* Description:
* </p>
*/
@Override
public void run() {
try {
ihotelInsuranceBatchService.sendInsuranceBatch(ihotelItem, remoteIp);
} catch (Exception e) {
IHotelLoggerUtil.error("获取保险确认号失败", e);
} } }
JUC回顾之-ThreadPoolExecutor的原理和使用的更多相关文章
- JUC回顾之-volatile的原理和使用
1.计算机内存模型的相关概念 计算机在执行程序时,每条指令都是在CPU中执行的,在指令的执行过程中,涉及到数据的读取和写入.由于程序在运行的过程中数据是放在"主存"中的, 由于数据 ...
- 硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的实现原理
前提 很早之前就打算看一次JUC线程池ThreadPoolExecutor的源码实现,由于近段时间比较忙,一直没有时间整理出源码分析的文章.之前在分析扩展线程池实现可回调的Future时候曾经提到并发 ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- JUC - Monitor监控ThreadPoolExecutor
JUC - Monitor监控ThreadPoolExecutor 一个自定义Monitor监控ThreadPoolExecutor的执行情况 TASK WokerTask class WorkerT ...
- JDK ThreadPoolExecutor核心原理与实践
一.内容概括 本文内容主要围绕JDK中的ThreadPoolExecutor展开,首先描述了ThreadPoolExecutor的构造流程以及内部状态管理的机理,随后用大量篇幅深入源码探究了Threa ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- Java - "JUC线程池" ThreadPoolExecutor原理解析
Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...
- JUC回顾之-线程池的原理和使用
Java并发编程:线程池的使用 Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程 ...
- JUC回顾之-CyclicBarrier底层实现和原理
1.CyclicBarrier 字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时候,屏障才会开门.所有被 ...
随机推荐
- 算法系列6《MAC》
1. 简介 MAC是使用命令的所有元素(包括命令头)产生的.一条命令的完整性,包括命令数据域(如果存在的话)中的数据元,通过安全报文传送得以保证.按照如下的方式使用单重或三重DEA加密方式产生MAC: ...
- ORA-00265: instance recovery required, cannot set ARCHIVELOG
OS: Oracle Linux Server release 5.7 DB: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - ...
- CentOS6.4上搭建hadoop-2.4.0集群
公司Commerce Cloud平台上提供申请主机的服务.昨天试了下,申请了3台机器,搭了个hadoop环境.以下是机器的一些配置: emi-centos-6.4-x86_64medium | 6GB ...
- 【javascript】html5中使用canvas编写头像上传截取功能
[javascript]html5中使用canvas编写头像上传截取功能 本人对canvas很是喜欢,于是想仿照新浪微博头像上传功能(前端使用canvas) 本程序目前在谷歌浏览器和火狐浏览器测试可用 ...
- Oracle ODP.NET连接池
数据库连接池 连接池是数据库连接的缓存,每当应用程序需要连接数据库时向连接池申请数据库连接,连接池负责具体数据库连接的创建和销毁.连接池中的数据库连接会缓存一段时间,后续的连接请求首先使用缓存中的数据 ...
- java中直接打印对象
java中直接打印对象,会调用对象.toString()方法.如果没有重写toString()方法会输出"类名+@+hasCode"值,hasCode是一个十六进制数 //没有重写 ...
- background-origin
background-origin 设置元素背景图片的原始起始位置. 语法: background-origin : border-box | padding-box | content-box; 参 ...
- android开发 ,对接支付宝,服务器(PHP)校验失败
已备忘记,资料链接: http://my.oschina.net/u/256646/blog/174222 注意: 里面有一个设计到支付宝公钥的地方: 注 意这个是2048位的公钥应该是9行或者10行 ...
- 撸一撸腾讯的微信支付(C#)
一.前言 以往网上支付都是支付宝的天下,随着微信用户群的日益增多(其实,到现在我也不理解微信为嘛那么火,功能还没QQ强大,或许是公众号的原因?),先如今不上个微信支付你都不好意思说你系统支持在线支付. ...
- 【CentOS】设置静态IP
问题描述: CentOS配置静态IP 问题解决: (1)修改IP--修改配置文件/etc/sysconfig/network-scripts/ifcfg-eth ...