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. Nginx 文件下载 apk 文件下载不了

    通过nginx 做下载服务器 下载 apk 安装包, 出现错误502和 499. 解决办法在 nginx的  mime.types 中 来自为知笔记(Wiz)

  2. 白话大数据 | Spark和Hadoop到底谁更厉害?

    要想搞清楚spark跟Hadoop到底谁更厉害,首先得明白spark到底是什么鬼. 经过之前的介绍大家应该非常了解什么是Hadoop了(不了解的点击这里:白话大数据 | hadoop究竟是什么鬼),简 ...

  3. centos6.9关闭防火墙

    /etc/init.d/iptables stop     临时关闭防火墙, chkconfig iptables off    永久关闭防火墙 查看防火墙状态  chkconfig --list i ...

  4. oracle中可能使用到的命令

    借鉴他人 1.su – oracle 不是必需,适合于没有DBA密码时使用,可以不用密码来进入sqlplus界面.2.sqlplus /nolog 或sqlplus system/manager 或. ...

  5. 20175305张天钰《java程序设计》第七周学习总结

    <java程序设计>第七周学习总结 第八章 常用实用类 1.String类 1.String类不可以有子类. 2.用户无法输出String对象的引用,输出的是字符序列 3.构造方法:Str ...

  6. Linux磁盘和文件系统管理

    1.检测并确认新硬盘 挂载好新的硬盘设备并启动主机后,Linux系统会自动检测并加载该硬盘,无须额外安装驱动.执行“fdisk -l”命令即可查看,确认新增硬盘的设备名称和位置.作用:列出当前系统中所 ...

  7. Linux 添加新用户账号并赋予root权限

    除了root用户之外,通常需要为每个管理创建各自的用户账号,方便每个管理员登录使用, 步骤如下: 1.  添加新用户账号 useradd  mary.lee 2.  为新用户账号设置密码 passwd ...

  8. BigDecimal.valueOf

    Those are two separate questions: "What should I use for BigDecimal?" and "What do I ...

  9. JSTree如何实现第二级菜单异步从数据库读取。

    参考文档: https://www.cnblogs.com/luozhihao/p/4679050.html http://jsfiddle.net/vakata/2kwkh2uL/5/ 核心的关键点 ...

  10. Flask消息验证与提示

    一,消息提示基本语法. 1,先新建一个Flask工作空间. 2,新建后自动得到一个app.py文件,直接运行可以看到基本效果.然后引入 from flask import flash.使用这个flas ...