最近项目中做到一个关于批量发短信的业务,如果用户量特别大的话,不能使用单线程去发短信,只能尝试着使用多任务来完成!我们的项目使用到了方式二,即Future的方案

Java 线程池
Java通过Executors提供四种线程池,分别为:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。 优点
重用存在的线程,减少对象创建、消亡的开销,性能佳。
可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
提供定时执行、定期执行、单线程、并发数控制等功能。
方式一(CountDownLatch)
public class StatsDemo {
final static SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"); final static String startTime = sdf.format(new Date()); /**
* IO密集型任务 = 一般为2*CPU核心数(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
* CPU密集型任务 = 一般为CPU核心数+1(常出现于线程中:复杂算法)
* 混合型任务 = 视机器配置和复杂度自测而定
*/
private static int corePoolSize = Runtime.getRuntime().availableProcessors();
/**
* public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
* TimeUnit unit,BlockingQueue<Runnable> workQueue)
* corePoolSize用于指定核心线程数量
* maximumPoolSize指定最大线程数
* keepAliveTime和TimeUnit指定线程空闲后的最大存活时间
* workQueue则是线程池的缓冲队列,还未执行的线程会在队列中等待
* 监控队列长度,确保队列有界
* 不当的线程池大小会使得处理速度变慢,稳定性下降,并且导致内存泄露。如果配置的线程过少,则队列会持续变大,消耗过多内存。
* 而过多的线程又会 由于频繁的上下文切换导致整个系统的速度变缓——殊途而同归。队列的长度至关重要,它必须得是有界的,这样如果线程池不堪重负了它可以暂时拒绝掉新的请求。
* ExecutorService 默认的实现是一个无界的 LinkedBlockingQueue。
*/
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1000)); public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
//使用execute方法
executor.execute(new Stats("任务A", 1000, latch));
executor.execute(new Stats("任务B", 1000, latch));
executor.execute(new Stats("任务C", 1000, latch));
executor.execute(new Stats("任务D", 1000, latch));
executor.execute(new Stats("任务E", 1000, latch));
latch.await();// 等待所有人任务结束
System.out.println("所有的统计任务执行完成:" + sdf.format(new Date()));
} static class Stats implements Runnable {
String statsName;
int runTime;
CountDownLatch latch; public Stats(String statsName, int runTime, CountDownLatch latch) {
this.statsName = statsName;
this.runTime = runTime;
this.latch = latch;
} public void run() {
try {
System.out.println(statsName+ " do stats begin at "+ startTime);
//模拟任务执行时间
Thread.sleep(runTime);
System.out.println(statsName + " do stats complete at "+ sdf.format(new Date()));
latch.countDown();//单次任务结束,计数器减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果

方式二(Future)

重点是和springboot整合,采用注解bean方式生成ThreadPoolTaskExecutor

@Bean

//spring依赖包
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class GlobalConfig { /**
* 默认线程池线程池
*
* @return Executor
*/
@Bean
public ThreadPoolTaskExecutor defaultThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程数目
executor.setCorePoolSize(16);
//指定最大线程数
executor.setMaxPoolSize(64);
//队列中最大的数目
executor.setQueueCapacity(16);
//线程名称前缀
executor.setThreadNamePrefix("defaultThreadPool_");
//rejection-policy:当pool已经达到max size的时候,如何处理新任务
//CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
//对拒绝task的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//线程空闲后的最大存活时间
executor.setKeepAliveSeconds(60);
//加载
executor.initialize();
return executor;
}
}
使用
//通过注解引入配置
@Resource(name = "defaultThreadPool")
private ThreadPoolTaskExecutor executor;
                        //使用Future方式执行多任务
//生成一个集合
List<Future> futures = new ArrayList<>(); //获取后台全部有效运营人员的集合
List<AdminUserMsgResponse> adminUserDOList = adminManagerService.GetUserToSentMsg(null); for (AdminUserMsgResponse response : adminUserDOList) {
//并发处理
if (response.getMobile() != null) {
Future<?> future = executor.submit(() -> {
//发送短信
mobileMessageFacade.sendCustomerMessage(response.getMobile(), msgConfigById.getContent());
});
futures.add(future);
}
} //查询任务执行的结果
for (Future<?> future : futureList) {
while (true) {//CPU高速轮询:每个future都并发轮循,判断完成状态然后获取结果,这一行,是本实现方案的精髓所在。即有10个future在高速轮询,完成一个future的获取结果,就关闭一个轮询
if (future.isDone()&& !future.isCancelled()) {//获取future成功完成状态,如果想要限制每个任务的超时时间,取消本行的状态判断+future.get(1000*1, TimeUnit.MILLISECONDS)+catch超时异常使用即可。
Integer i = future.get();//获取结果
System.out.println("任务i="+i+"获取完成!"+new Date());
list.add(i);
break;//当前future获取结果完毕,跳出while
} else {
Thread.sleep(1);//每次轮询休息1毫秒(CPU纳秒级),避免CPU高速轮循耗空CPU---》新手别忘记这个
}
}
}

springboot之多任务并行+线程池处理的更多相关文章

  1. SpringBoot开发案例之多任务并行+线程池处理

    前言 前几篇文章着重介绍了后端服务数据库和多线程并行处理优化,并示例了改造前后的伪代码逻辑.当然了,优化是无止境的,前人栽树后人乘凉.作为我们开发者来说,既然站在了巨人的肩膀上,就要写出更加优化的程序 ...

  2. 玩转SpringBoot之定时任务@Scheduled线程池配置

    序言 对于定时任务,在SpringBoot中只需要使用@Scheduled 这个注解就能够满足需求,它的出现也给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了. 但是, ...

  3. SpringBoot普通消息队列线程池配置

    1 package com.liuhuan.study.config; 2 3 import com.google.common.util.concurrent.ThreadFactoryBuilde ...

  4. SpringBoot 自定义线程池

    本教程目录: 自定义线程池 配置spring默认的线程池 1. 自定义线程池 1.1 修改application.properties task.pool.corePoolSize=20 task.p ...

  5. SpringBoot 线程池配置 实现AsyncConfigurer接口方法

      目的是:  通过实现AsyncConfigurer自定义线程池,包含异常处理  实现AsyncConfigurer接口对异常线程池更加细粒度的控制 *a) 创建线程自己的线程池  b) 对void ...

  6. [开源项目]可观测、易使用的SpringBoot线程池

    在开发spring boot应用服务的时候,难免会使用到异步任务及线程池.spring boot的线程池是可以自定义的,所以我们经常会在项目里面看到类似于下面这样的代码 @Bean public Ex ...

  7. Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池

    前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...

  8. linux 条件变量与线程池

    条件变量Condition Variables 概述 1. 条件变量提供了另外一种线程同步的方式.如果没有条件变量,程序需要使用线程连续轮询(可能在临界区critical section内)方式检查条 ...

  9. springboot线程池任务调度类 -- ThreadPoolTaskScheduler介绍

    springboot中有一个bean,ThreadPoolTaskScheduler,可以很方便的对重复执行的任务进行调度管理:相比于通过java自带的周期性任务线程池ScheduleThreadPo ...

随机推荐

  1. POJ 3264 Balanced Lineup 区间最值

    POJ3264 比较裸的区间最值问题.用线段树或者ST表都可以.此处我们用ST表解决. ST表建表方法采用动态规划的方法, ST[I][J]表示数组从第I位到第 I+2^J-1 位的最值,用二分的思想 ...

  2. Request returned failure status 401

    Request returned failure status 401.Invalid OpenStack Identity credentials.

  3. 68. 对Extjs中store的多种操作

    转自:https://www.cnblogs.com/exmyth/archive/2013/05/16/3082045.html 先来个声明,看着不错,贴过来的,没都测试过. Store.getCo ...

  4. IP V4 和 IP V6 初识

    IP V4    是互联网协议的第四版 地址长度为32位,4字节,用十进制表示 格式为:A.B.C.D 最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展 IP V6    是互联网协议的I ...

  5. 【转】Java - printf

    [转自]http://heidian.iteye.com/blog/404632 目前printf支持以下格式:            %c        单个字符            %d     ...

  6. JAVA基础(多线程Thread和Runnable的使用区别(转载)

    转自:http://jinguo.iteye.com/blog/286772 Runnable是Thread的接口,在大多数情况下“推荐用接口的方式”生成线程,因为接口可以实现多继承,况且Runnab ...

  7. struts2基础学习----struts.xml配置文件

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "- ...

  8. Python机器学习算法 — KNN分类

    KNN简介 K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一.KNN分类算法属于监督学习. 最简单最初级的分类器是将全部的训练 ...

  9. html5——动画案例(太阳系)

    太阳系主要利用定位,伪元素 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  10. (转)Hibernate关联映射——对象的三种关系

    http://blog.csdn.net/yerenyuan_pku/article/details/70148618 Hibernate关联映射——对象的三种关系 Hibernate框架基于ORM设 ...