初学者很容易看错,如果没有看到spring或者JUC源码的人肯定是不太了解的。

ThreadPoolTaskExecutor是spring core包中的,而ThreadPoolExecutor是JDK中的JUC。ThreadPoolTaskExecutor是对ThreadPoolExecutor进行了封装处理。

自己在之前写多线程代码的时候都是这么玩的executor=Executors.newCachedThreadPool();但是有一次在大量数据的时候由于入库速度远大于出库速度导致内存急剧膨胀最后悲剧了重写代码,原来spring 早就给我们做好封装了。

来看一下ThreadPoolExecutor结构,祖类都是调用Executor接口:

再来看一下ThreadPoolTaskExecutor结构,祖类都是调用Executor接口:

再来看一下源码:

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements SchedulingTaskExecutor {
private final Object poolSizeMonitor = new Object();
private int corePoolSize = 1;
private int maxPoolSize = 2147483647;
private int keepAliveSeconds = 60;
private boolean allowCoreThreadTimeOut = false;
private int queueCapacity = 2147483647;
private ThreadPoolExecutor threadPoolExecutor; //这里就用到了ThreadPoolExecutor
//下面是设置完配置需要调用initialize方法初始化
@Override
protected ExecutorService initializeExecutor(
ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
queue, threadFactory, rejectedExecutionHandler);
if (this.allowCoreThreadTimeOut) {
executor.allowCoreThreadTimeOut(true);
} this.threadPoolExecutor = executor;
return executor;
}

这是ThreadPoolTaskExecutor用来初始化threadPoolExecutor的方法,BlockingQueue是一个阻塞队列,这个我们先不管。由于ThreadPoolTaskExecutor的实现方式完全是使用threadPoolExecutor进行实现,我们需要知道这个threadPoolExecutor的一些参数。

 public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

     int corePoolSize:线程池维护线程的最小数量. 
  int maximumPoolSize:线程池维护线程的最大数量. 
  long keepAliveTime:空闲线程的存活时间. 
  TimeUnit unit: 时间单位,现有纳秒,微秒,毫秒,秒枚举值. 
  BlockingQueue<Runnable> workQueue:持有等待执行的任务队列. 
  RejectedExecutionHandler handler: 
  用来拒绝一个任务的执行,有两种情况会发生这种情况。 
  一是在execute方法中若addIfUnderMaximumPoolSize(command)为false,即线程池已经饱和; 
  二是在execute方法中, 发现runState!=RUNNING || poolSize == 0,即已经shutdown,就调用ensureQueuedTaskHandled(Runnable command),在该方法中有可能调用reject。

ThreadPoolExecutor池子的处理流程如下:  

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

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

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

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

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

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

 <!-- 异步线程池 -->
<bean id="threadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心线程数,默认为1 -->
<property name="corePoolSize" value="3" />
<!-- 最大线程数,默认为Integer.Max_value -->
<property name="maxPoolSize" value="10" />
<!-- 队列最大长度 >=mainExecutor.maxSize -->
<property name="queueCapacity" value="25" />
<!-- 线程池维护线程所允许的空闲时间 -->
<property name="keepAliveSeconds" value="300" />
<!-- 线程池对拒绝任务(无线程可用)的处理策略 ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃. -->
<property name="rejectedExecutionHandler">
        <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
        <!-- CallerRunsPolicy:若已达到待处理队列长度,将由主线程直接处理请求 -->
        <!-- DiscardOldestPolicy:抛弃旧的任务;会导致被丢弃的任务无法再次被执行 -->
        <!-- DiscardPolicy:抛弃当前任务;会导致被丢弃的任务无法再次被执行 -->
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>

Reject策略预定义有四种: 
(1)ThreadPoolExecutor.AbortPolicy策略,是默认的策略,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。 
(2)ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃. 
(3)ThreadPoolExecutor.DiscardPolicy策略,不能执行的任务将被丢弃. 
(4)ThreadPoolExecutor.DiscardOldestPolicy策略,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程).

关于callable回调方法(因为为队列阻塞,如果到取值某个执行的值会等待执行完成)

        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(5);
threadPoolTaskExecutor.setMaxPoolSize(50);
threadPoolTaskExecutor.initialize(); List<String> paymentSeqNoList = new ArrayList<>();
for (int i = 0; i <; i++) {
paymentSeqNoList.add(String.valueOf(i));
}
Long startTime = System.currentTimeMillis();
Map<String, FutureTask<String>> futureMap = new HashMap<String, FutureTask<String>>();
//线程池提交返回
for (String paymentSeqNo : paymentSeqNoList) {
FutureTask<String> futureTask = new FutureTask<String>(new MyTestCallable(paymentSeqNo));
futureMap.put(paymentSeqNo, futureTask);
// submit提交执行
threadPoolTaskExecutor.submit(futureTask);
}
Long endTime = System.currentTimeMillis();
System.out.println("耗时1:" + (endTime - startTime));
private static class MyTestCallable implements Callable<String> {
private final String paymentSeqNo; /**
* 构造
*
* @param
*/
public MyTestCallable(String paymentSeqNo) {
this.paymentSeqNo = paymentSeqNo;
} /*
* (non-Javadoc)
* @see java.util.concurrent.Callable#call()
*/
public String call() throws Exception {
ControlRequest controlRequest = new ControlRequest(paymentSeqNo, CtrlType.RETRY, null);
//System.out.println(controlRequest);
return "成功";
}
}

关于callable回调值监听是否成功,JDK1.8 也开始支持guava方法了,guava有ListenableFuture 返回优化如下:

  Long startTime2 = System.currentTimeMillis();
ListenableFuture<String> listenableFuture = null;
for (String paymentSeqNo : paymentSeqNoList) {
ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
listenableFuture = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "成功";
}
});
}
//监听事件
Futures.addCallback(listenableFuture, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("get listenable future's result with callback " + result);
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
Long endTime2 = System.currentTimeMillis();
System.out.println("耗时2:" + (endTime2 - startTime2));

JAVA线程池学习,ThreadPoolTaskExecutor和ThreadPoolExecutor有何区别?的更多相关文章

  1. Java线程池学习

    Java线程池学习 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java ...

  2. Java线程池学习总结

    一 使用线程池的好处 池化技术相比大家已经屡见不鲜了,线程池.数据库连接池.Http 连接池等等都是对这个思想的应用.池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率. 线程池提供了 ...

  3. Java线程池学习心得

    一.普通线程和线程池的对比 new Thread的弊端如下: a. 每次new Thread新建对象性能差.b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或o ...

  4. Java 线程池学习

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.真正的线程池接口是ExecutorService. 下面这张图完整描述了线程 ...

  5. 【Java多线程】线程池学习

    Java线程池学习 众所周知,Java不仅提供了线程,也提供了线程池库给我们使用,那么今天来学学线程池的具体使用以及线程池基本实现原理分析. ThreadPoolExecutor ThreadPool ...

  6. 转:JAVA线程池ThreadPoolExecutor与阻塞队列BlockingQueue

    从Java5开始,Java提供了自己的线程池.每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池.以下是我的学习过程. 首先是构 ...

  7. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  8. Java 线程池框架核心代码分析

    前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...

  9. 【Java线程池快速学习教程】

    1. Java线程池 线程池:顾名思义,用一个池子装载多个线程,使用池子去管理多个线程. 问题来源:应用大量通过new Thread()方法创建执行时间短的线程,较大的消耗系统资源并且系统的响应速度变 ...

随机推荐

  1. data.go

    {         return ErrList(errs)     }     return nil }

  2. BZOJ_3524_[Poi2014]Couriers_主席树

    BZOJ_3524_[Poi2014]Couriers_主席树 题意:给一个长度为n的序列a.1≤a[i]≤n. m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r- ...

  3. STM32F1固件库文件讲解与基于固件库新建MDK工程模板

    操作系统:win10 1.文件目录 (在cmd下用"cd 文件夹" 进入到要显示的文件夹,如cd d:\en.stsw-stm32054,然后输入tree 回车就会出现上图的目录结 ...

  4. BF-9000 BMC任务关键型应急通信系统

    一.系统简介 BF-9000 BMC任务关键型应急通信系统,凝聚北峰通信近30年专网通信与应急通信研发的经验,并结合用户实际需求和应用场景所打造. 整体设计思路是采用骨干网.前指网.分队战斗网三层组网 ...

  5. 虚拟机设置静态IP配置

    网关 虚拟机的ip设置  vi /etc/sysconfig/network-scripts/ifcfg-eth0 重启网卡 service network restart 测试 ping www.b ...

  6. k8s经典实战—搭建WordPress

    k8s经典实战—搭建WordPress说明:需要在k8s上部署lnmp环境,建议跟着步骤来端口最好不要改,希望你也能搭建成功,完成这个搭建后你对Kubernetes的技术基本上是入门了.首先看下效果图 ...

  7. 一个C#程序员学习微信小程序的笔记

    客户端打开小程序的时候,就将代码包下载到本地进行解析,首先找到了根目录的 app.json ,知道了小程序的所有页面. 在这个Index页面就是我们的首页,客户端在启动的时候,将首页的代码装载进来,通 ...

  8. 前端笔记之Canvas

    一.Canvas基本使用 Canvas是HTML5的画布,Canvas算是“不务正业”的面向对象大总结,将面向对象玩极致. 算法为王!就是说canvas你不会,但是算法好,不怕写业务,不怕代码量,只要 ...

  9. c# 接口相同方法申明使用

    using System; namespace ConsoleApp1 { interface IInterface1 { void ft(); } interface IInterface2 { v ...

  10. 设计模式之组合模式——Java语言描述

    组合模式又叫做部分整体模式,适用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次 介绍 意图 将对象组合成树形结构以表示"部分-整体" ...