为什么要用线程池?

服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

构建服务器应用程序的一个过于简单的模型应该是:每当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上,对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。

每个请求对应一个线程(thread-per-request)方法的不足之一是:为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源(线程的生命周期!)。在一个JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

使用线程池的风险

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足,并发错误,线程泄漏,请求过载。

有效使用线程池的准则

(1)不要对那些同步等待其它任务结果的任务排队。这可能会导致上面所描述的那种形式的死锁,在那种死锁中,所有线程都被一些任务所占用,这些任务依次等待排队任务的结果,而这些任务又无法执行,因为所有的线程都很忙。

(2)在为时间可能很长的操作使用合用的线程时要小心。如果程序必须等待诸如 I/O 完成这样的某个资源,那么请指定最长的等待时间,以及随后是失效还是将任务重新排队以便稍后执行。这样做保证了:通过将某个线程释放给某个可能成功完成的任务,从而将最终取得 某些 进展。

理解任务。要有效地调整线程池大小,您需要理解正在排队的任务以及它们正在做什么。它们是 CPU 限制的(CPU-bound)吗?它们是 I/O 限制的(I/O-bound)吗?您的答案将影响您如何调整应用程序。如果您有不同的任务类,这些类有着截然不同的特征,那么为不同任务类设置多个工作队 列可能会有意义,这样可以相应地调整每个池。

线程池的创建

我们可以通过java.util.concurrent.ThreadPoolExecutor来创建一个线程池。

常用构造方法为:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)

  • corePoolSize: 线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池维护线程所允许的空闲时间
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 线程池对拒绝任务的处理策略

处理任务的优先级

线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
线程数量达到了corePools,则将任务移入队列等待
队列已满,新建线程(非核心线程)执行任务
队列已满,总线程数又达到了maximumPoolSize,就会抛出异常

handler有四个选择

ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy() : 重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy() : 抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy() : 抛弃当前的任务

ThreadPoolTaskExecutor

四个属性在org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor里面都是传递给了java.util.concurrent.ThreadPoolExecutor 所以基本上线程的创建、任务的提交、任务的执行、线程的销毁都是ThreadPoolExecutor来做的

ThreadPoolTaskExecutor 使用

pom.xml

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.0.5.RELEASE</version>
</dependency> 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "/spring-beans.dtd">
<beans><bean id ="taskExecutor"  class ="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" ><property name ="corePoolSize" value ="5" /> <!--核心线程数 --><property name ="keepAliveSeconds" value ="3000" /> <!-- 某线程空闲超过这个时间,就回收该线程 --><property name ="maxPoolSize" value ="10" />     <!--最大线程数 --><property name ="queueCapacity" value ="1000" />  <!-- 队列大小 --><property name= "rejectedExecutionHandler" ><!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 --><!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 --><!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 --><!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 --><bean class = "java.util.concurrent.ThreadPoolExecutor$DiscardPolicy"   /></property>
</bean></beans>

SpringThread.java

public class SpringThread extends Thread{private int parameter;public SpringThread(int parameter){this.parameter = parameter;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":执行了..." + parameter);try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}
}

测试代码

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.task.TaskExecutor;public class App
{public static void main( String[] args ){//System.out.println( "Hello World!" );ApplicationContext appContext = new ClassPathXmlApplicationContext("file:applicationContext.xml");TaskExecutor executor = (TaskExecutor) appContext.getBean("taskExecutor");for (int i = 0; i < 10; i++) {SpringThread t = new SpringThread(i);executor.execute(t);}System.out.println("main process is finish .....");}
}

运行结果:

taskExecutor-1:执行了...0
taskExecutor-2:执行了...1
taskExecutor-3:执行了...2
taskExecutor-4:执行了...3
taskExecutor-5:执行了...4
main process is finish .....
taskExecutor-1:执行了...5
taskExecutor-3:执行了...7
taskExecutor-4:执行了...8
taskExecutor-2:执行了...6
taskExecutor-5:执行了...9

转载源:
(1)http://www.cnblogs.com/xinxindiandeng/p/6383311.html
(2)http://blog.csdn.net/benbendy1984/article/details/54932267

ThreadPoolTaskExecutor 使用和原理相关推荐

  1. spring aop实现原理_Spring 异步实现原理与实战分享

    最近因为全链路压测项目需要对用户自定义线程池 Bean 进行适配工作,我们知道全链路压测的核心思想是对流量压测进行标记,因此我们需要给压测的流量请求进行打标,并在链路中进行传递,那么问题来了,如果项目 ...

  2. Spring异步调用原理及SpringAop拦截器链原理

    一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTI ...

  3. java任务追踪预警怎么写_分布式系统中如何优雅地追踪日志(原理篇)

    本文只讲原理,不讲框架. 分布式系统中日志追踪需要考虑的几个点? 需要一个全服务唯一的id,即traceId,如何保证? traceId如何在服务间传递? traceId如何在服务内部传递? trac ...

  4. Spring学习总结(29)——Spring异步处理@Async的使用以及原理、源码分析(@EnableAsync)

    在开发过程中,我们会遇到很多使用线程池的业务场景,例如异步短信通知.异步记录操作日志.大多数使用线程池的场景,就是会将一些可以进行异步操作的业务放在线程池中去完成.例如在生成订单的时候给用户发送短信, ...

  5. Redis 发布订阅原理以及springboo中RedisTemplate集成

    一.Redis发布订阅原理 Redis的架构包括两个部分:Redis Client和Redis Server,即客户端和服务端.客户端负责向服务器端发送请求并接受来自服务器端的响应.服务器端负责处理客 ...

  6. CGB2005 JT-1(jt概述 SqlYog 物理模型图PD 表结构 pom文件标签说明 jt环境搭建 创建项目2种,创建各种文件 idea导入,打包,删除项目,启动原理)

    注意事项: 1.京淘项目概述和动吧项目缺点描述 2.把sql文件导入数据库:通过SqlYog可视化工具或者Dos命令窗口. 3.SqlYog说明,和制作物理模型图的工具pd用发. 4.表结构学习 5. ...

  7. Springboot启动配置原理

    Springboot启动配置原理 启动配置原理 核心启动配置参数 配置在META-INF/spring.factories中 ApplicationContextInitializer SpringA ...

  8. sleuth原理详解

    sleuth原理 1.关键术语 Span:Span基本工作单元,发送一个远程调度任务就会产生一个Span, Span 是用 64ID唯一标识的,Trace是用另一个64ID唯一标识的.Span还包含了 ...

  9. UUID的使用及其原理

    今天敲项目要用UUID,想起之前老师告诉UUID的使用,但没说具体的生成逻辑,于是我进行了百度 首先,UUID的使用: //生成随机的UUID String uuid = UUID.randomUUI ...

最新文章

  1. linux+bash+参数脚本名,linux – Bash中的脚本参数
  2. ORM对mysql数据库中数据进行操作报错解决
  3. 在OpenCV环境下写的两个图像平移C和C++源代码
  4. C++女程序员一个人留在北京
  5. NetSuite ERP软件系统特点介绍!
  6. 上位机与欧姆龙PLC的Fins tcp通讯
  7. 泛微e9隐藏明细表_泛微e-cology的Ecode二次开发无侵入定制说明
  8. CPCL简易打印模板设计
  9. postgresql 日期相减
  10. 一个字形容大数据_中国大学校训大数据:最短的2个字,最长的28个字,10大高频汉字...
  11. 服务器虚拟资源池,大型医院基于Hyper-V的虚拟化服务器资源池构建
  12. 常用计算机网络技术缩写词和术语
  13. CameraX Java 1.0.0-alpha10 安卓开发
  14. 2-1 windows软件 --- x-shell/seurecrt/puty
  15. 致終將失去的手机形态
  16. app获取缓存、清理缓存
  17. 22吉大计算机学硕考研389分经验分享
  18. 交通标志牌检测--限速数字框选--图像处理 matlab
  19. 运营商精准大数据获客 快速精准触达目标用户
  20. 电脑密码忘记了? 使用U盘启动破解电脑密码

热门文章

  1. codeforces 1350.Div2 A-D Orac and xxx
  2. 单反相机tf卡用sd卡套稳定吗_解了摄影师的燃眉之急:入手雷克沙TF卡,一卡多用速度超快...
  3. PHP codesniffer 配置,如何配置PHP CodeSniffer让我的case语句按照我喜欢的方式缩进?...
  4. 《自动控制原理》个人笔记
  5. Docker学习笔记-概念和常见命令
  6. MOS管功率放大电路图和互补推挽结构分析-KIA MOS管
  7. 华为云“DDoS高防+CDN”联动
  8. mysql-常用sql语句基础
  9. 精品素材:15套免费的 Photoshop 自定义图形集
  10. 用c语言循环读取坐标,C语言之For循环--1(星状图)