1,需求:使用多线程批量发送邮件

需要批量发送邮件大概400封左右,但是因为发送邮件受网络限制,所以经常导致等待超时。所以就想到了使用多线程来发邮件,因为是异步的所以返回结果不受发邮件影响。

2,思路:使用spring的ThreadPoolTaskExecutor,用10个线程循环400个任务,完成任务后关闭

首先创建一个发邮件的Task(任务),只是负责发邮件。

然后创建一个执行任务的类,让ThreadPoolTaskExecutor循环执行

创建一个ThreadPoolTaskExecutor的bean配置交给spring管理

3,代码:SendEmailTask.java(任务类) SendEmailThread.java(执行任务类) email.xml(ThreadPoolTaskExecutor的配置)

SendEmailTask.java:

package com.XXX.core.base.utils.task;

import com.XXX.core.base.utils.EmailUtil;

/**
* 发送邮件任务类
* @author chenminchang
* @date 2017年3月20日 下午4:40:42
*/
public class SendEmailTask implements Runnable {

	private String toemail;
	private String title;
	private String content;

	public SendEmailTask(String toemail, String title, String content){
		this.toemail = toemail;
		this.title = title;
		this.content = content;
	}

	@Override
	public void run() {
		EmailUtil.send(toemail, title, content);//send是自行封装的发邮件的方法
	}

}

SendEmailThread.java:

package com.XXX.core.base.utils.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
* 多线程批量发送邮件
* @author chenminchang
* @date 2017年3月20日 下午4:54:35
*/
public class SendEmailThread {

	private static Logger log = LoggerFactory.getLogger(SendEmailThread.class);
	private String[] toEmails;//需要发送的邮箱
	private String title;//需要发送的标题
	private String content;//需要发送的内容
	private ApplicationContext ctx =  new ClassPathXmlApplicationContext("classpath*:**/applicationContext**.xml");
	private ThreadPoolTaskExecutor poolTaskExecutor = (ThreadPoolTaskExecutor)ctx.getBean("threadPool");

	public SendEmailThread(String title, String content, String... toEmails) {
		this.toEmails = toEmails;
		this.title = title;
		this.content = content;
	}

	/**
	 * 使用多线程执行任务
	 *
	 * @author chenminchang
	 * @create 2017年3月22日上午10:50:31
	 */
	public void runTask() {
		if (poolTaskExecutor == null) {
			log.debug("the poolTaskExecutor is null");
		} else {
			if (toEmails != null && toEmails.length > 0) {
				if (toEmails.length < poolTaskExecutor.getCorePoolSize())//当任务数小于创建的线程
					poolTaskExecutor.setCorePoolSize(toEmails.length);
				for (String email : toEmails) {
					try {
						poolTaskExecutor.execute(getNextTask(email, this.title , this.content));
					} catch (Exception ex) {
						 ex.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * 获取新任务
	 * @param toemail
	 * @param title
	 * @param content
	 * @return
	 * @author chenminchang
	 * @create 2017年3月20日下午5:15:07
	 */
	private Runnable getNextTask(String toemail, String title, String content) {
		return new SendEmailTask(toemail, title, content);
	}

}

threadPool.xml: 将此文件添加到applicationContent.xml相同的文件夹下

<?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:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="threadPool"
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<!-- 核心线程数,默认为1 -->
		<property name="corePoolSize" value="10" />

		<!-- 最大线程数,默认为Integer.MAX_VALUE -->
		<property name="maxPoolSize" value="100" />

		<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
		<property name="queueCapacity" value="500" />

		<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
		<property name="keepAliveSeconds" value="60" />

		<!-- 完成任务自动关闭 , 默认为false-->
		<property name="waitForTasksToCompleteOnShutdown" value="true" />

		<!-- 核心线程超时退出,默认为false -->
		<property name="allowCoreThreadTimeOut" value="true" />

		<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
		<property name="rejectedExecutionHandler">
			<!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 -->
			<!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 -->
			<!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
			<!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 -->
			<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
		</property>
	</bean>
</beans>

这里需要注意的是如果没有allowCoreThreadTimeOut这项配置,即使waitForTasksToCompleteOnShutdown设为true,线程完毕是不会关闭的,只是会转为驻留状态,还有一点是每次创建的线程都会产生新的线程,老线程不关闭,新线程重新生成,这样导致线程越来越多。大家可以根据Java VisualVM去测试,所以必须加上allowCoreThreadTimeOut属性

然后需要注意的是在 applicationContent.xml中引入threadPool.xml,在applicationContext.xml中加入 <import resource="threadPool.xml" />

4,测试SendEmailThreadTest.java

package com.XXX.core.base.utils.task;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.XXX.core.base.utils.EmailUtil;

/**
*
* @author chenminchang
* @date 2017年3月20日 下午6:39:42
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:**/applicationContext**.xml" })
public class SendEmailThreadTest {

	/**
	 * 测试线程批量发送邮件
	 *
	 * @author chenminchang
	 * @create 2017年3月22日上午11:59:48
	 */
	@Test
	public void testSendEmailThread() {
		List<String> toEmailList = new ArrayList<>();
		for (int i = 0; i < 50; i++) {
			toEmailList.add("你的@邮箱");
		}
		EmailUtil.sendBatchUseThread("注册用户", "您已成为注册用户", toEmailList.toArray(new String[toEmailList.size()]));
	}
}

5,结果

6,推荐

这里给大家推荐一篇写的不错的关于线程池配置的文章:线程池ThreadPoolExecutor参数设置

Spring多线程批量发送邮件(ThreadPoolTaskExecutor)的更多相关文章

  1. spring多线程与定时任务

    本篇主要描述一下spring的多线程的使用与定时任务的使用. 1.spring多线程任务的使用 spring通过任务执行器TaskExecutor来实现多线程与并发编程.通常使用ThreadPoolT ...

  2. spring batch批量处理框架

    spring batch精选,一文吃透spring batch批量处理框架 前言碎语 批处理是企业级业务系统不可或缺的一部分,spring batch是一个轻量级的综合性批处理框架,可用于开发企业信息 ...

  3. spring多线程初探

    6月14号  晴  最高温度37   今天很热的一天啊,开发的任务现在正在测试阶段,手头没有什么工作任务,忙里偷闲,丰富一下我的blog. 前两天有个需求:调用第三方接口,这个接口的响应时间有点长,需 ...

  4. PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)

    源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...

  5. spring 多线程 注入 服务层 问题

    在用多线程的时候,里面要用到Spring注入服务层,或者是逻辑层的时候,一般是注入不进去的.具体原因应该是线程启动时没有用到Spring实例不池.所以注入的变量值都为null. 详细:http://h ...

  6. HBase 高性能获取数据(多线程批量式解决办法) + MySQL和HBase性能测试比较

    摘要:   在前篇博客里已经讲述了通过一个自定义 HBase Filter来获取数据的办法,在末尾指出此办法的性能是不能满足应用要求的,很显然对于如此成熟的HBase来说,高性能获取数据应该不是问题. ...

  7. .net调用Outlook 批量发送邮件,可指定Outlook中的账号来发送邮件

    .net调用Outlook 批量发送邮件,可指定Outlook中的账号来发送邮件 源码可以在我的资源列表中下载: MPOEMail http://download.csdn.net/my VS2012 ...

  8. 项目一:第十四天 1.在realm中动态授权 2.Shiro整合ehcache 缓存realm中授权信息 3.动态展示菜单数据 4.Quartz定时任务调度框架—Spring整合javamail发送邮件 5.基于poi实现分区导出

    1 Shiro整合ehCache缓存授权信息 当需要进行权限校验时候:四种方式url拦截.注解.页面标签.代码级别,当需要验证权限会调用realm中的授权方法   Shiro框架内部整合好缓存管理器, ...

  9. java多线程批量读取文件(七)

    新公司入职一个多月了,至今没有事情可以做,十来个新同事都一样抓狂,所以大家都自己学习一些新东西,我最近在看zookeeper,感觉蛮不错的,和微服务的zuul以及eureka功能类似,只是代码复杂了一 ...

随机推荐

  1. pip命令

    安装包 pip install django   #最新版本 pip install django==1.11.7 #指定版本 pip install django>=1.11.7 #最小版本 ...

  2. 《连载 | 物联网框架ServerSuperIO教程》- 16.集成OPC Server,及使用步骤。附:3.3 发布与版本更新说明。

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  3. 浏览器音频兼容和ffmpeg的音频转码使用

    浏览器对各音/视频格式的支持问题 浏览器测试效果图 ffmpeg在音频格式转换,和从视频中提取音频的简单实用 1.百度搜索浏览器对于音频文件的兼容,排在前面的文章大部分是复制粘贴很久以前的文章,容易误 ...

  4. python分支——if

    单分支判断 age = 16 if age >= 18: 判断语句,判断age是否大于等于18,注意if后面要加空格,条件写完后要加: print("你已经成年") prin ...

  5. 记录NPOI使用方法

    DLL 下载地址:https://files.cnblogs.com/files/xujunbao/NPOI.rar using NPOI.HSSF.UserModel; using NPOI.SS. ...

  6. 微软开源大规模数据处理项目 Data Accelerator

    微软开源了一个原为内部使用的大规模数据处理项目 Data Accelerator.自 2017 年开发以来,该项目已经大规模应用在各种微软产品工作管道上. 据微软官方开源博客介绍,Data Accel ...

  7. Docker入门到实践

    1.什么是Docke 1.网上有很多 2.为什么要使用Docker? 优点 更高效的利用系统资源 更快速的启动时间 一致的运行环境 持续交付和部署 更轻松的迁移 更轻松的维护和扩展 3.Docker的 ...

  8. jmeter主要函数助手功用说明

    jmeter中虽然有很多的插件,但是有些需要安装,有些具有一定的局限性.函数助手是一个快捷的工具库.下面记录一下函数助手中一些主要的函数的使用方法. 注:不内容中所有的实例均基于3.2记录 1._Be ...

  9. MDK的一些小应用

    一:MDK生成bin文件 Options(魔术棒)  ->  User  ->  After Build/rebuild  ->  Run#1(前边打钩) (后边的方框输入一段内容) ...

  10. C# 串口操作系列(5)--通讯库雏形

    C# 串口操作系列(5)--通讯库雏形 标签: 通讯c#数据分析byteclassstring 2010-08-09 00:07 21378人阅读 评论(73) 收藏 举报  分类: 通讯类库设计(4 ...