• 前言

    最近在做一个用户反馈的功能,就是当用户反馈意见或建议后服务端将意见保存然后发邮件给相关模块的开发者。考虑发邮件耗时的情况所以我想用异步的方法去执行,于是就开始研究Spring的@Async了。但网上的许多教程都是相互抄袭且直接复制代码的,根本没有去自己实践或者更改代码,所以在这两天我遇到了许多问题,使得@Async无效,也一直没有找到很好的文章去详细的说明@Async的正确及错误的使用方法及需要注意的地方,这里Sring是以配置文件的形式来开启@Async,而SpringBoot则是以注解的方式开启。
  • 教程
  1. Spring配置文件

    配置文件的话有两种方式一种是精简式:
    直接在applicationContext.xml中加入开启异步并添加task的命名空间
    <task:executor id="WhifExecutor" pool-size="10"/>
    <task:annotation-driven executor="WhifExecutor" />
    xmlns:task="http://www.springframework.org/schema/task"
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task.xsd

    这样好处是简单快速,缺点是线程池配置简单,线程创建不能定时关闭。

    所以我推荐下面这种,定义一个线程池,然后引入。其实两种方法原理是一样只不过下面这种只是线程池配置更加详细,线程在空闲后会根据存活时间配置进行关闭。

    在applicationContext.xml同目录下创建文件threadPool.xml内容如下,然后在applicationContext.xml中引入threadPool.xml:<import resource="threadPool.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
                            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
    
    	<!-- 开启异步,并引入线程池 -->
    	<task:annotation-driven executor="threadPool" />
    
    	<!-- 定义线程池 -->
    	<bean id="threadPool"
    		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    		<!-- 核心线程数,默认为1 -->
    		<property name="corePoolSize" value="5" />
    
    		<!-- 最大线程数,默认为Integer.MAX_VALUE -->
    		<property name="maxPoolSize" value="20" />
    
    		<!-- 队列最大长度,一般需要设置值>=notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
    		<property name="queueCapacity" value="500" />
    
    		<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
    		<property name="keepAliveSeconds" value="30" />
    
    		<!-- 完成任务自动关闭 , 默认为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>
  2. SpringBoot则是添加@EnableAsync注解
    @EnableAsync
    @SpringBootApplication
    @ServletComponentScan
    @MapperScan("com.cmc.schedule.model.mapper") //配置扫描mapper接口的地址
    public class NLPApplication extends SpringBootServletInitializer {
    
    	//不使用springboot内嵌tomcat启动方式
    	@Override
    	protected SpringApplicationBuilder configure(
    			SpringApplicationBuilder application) {
    		return application.sources(NLPApplication.class);
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(NLPApplication.class, args);
    	}
    
    	//默认使用fastjson解析
    	@Bean
    	public HttpMessageConverters fastJsonHttpMessageConverters() {
    		FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
    		FastJsonConfig fastJsonConfig = new FastJsonConfig();
    		fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    		fastConverter.setFastJsonConfig(fastJsonConfig);
    		HttpMessageConverter<?> converter = fastConverter;
    		return new HttpMessageConverters(converter);
    	}
    }
  3. 这一步Spring和SpringBoot都是一样的,创建一个异步方法的类
    package com.cmc.tst;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    /**
     * @Component 注解必须要有,否则无法将此bean注入
     * 当然也可以使用其他的注解,只要可以装配就行
     *
     * @author chenmc
     * @date 2017年9月4日 下午3:38:29
     */
    @Component
    public class MyAsync {
    
    	/**
    	 * @Async 表明这是一个异步方法,也就是说当调用这个方法时,
    	 * spring会创建一条线程来执行这个方法。
    	 * 注意:不能使用static来修饰此方法,否则@Async无效
    	 *
    	 * @author chenmc
    	 * @date 2017年9月4日 下午3:34:24
    	 */
    	@Async
    	public void asyncMethod(){
    		System.out.println(Thread.currentThread().getName());
    	}
    }
  4. 测试调用异步方法,我这里使用的是JUnit4测试
    	@Autowired
    	MyAsync async;
    
    	@Test
    	public void test() {
    		System.out.println(Thread.currentThread().getName() + "start");
    		//MyAsync async = new MyAsync(); //自己new出来的对象@Async将无效,必须要spring注入的
    		async.asyncMethod();
    		try {
    			Thread.sleep(500);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		System.out.println(Thread.currentThread().getName() + "end");
    	}
  5. 总结

    这里总结一下@Async注解无效的可能点

    一、异步方法使用static修饰

    二、异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类

    三、测试异步方法不能与异步方法在同一个类中

    四、测试类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

    五、如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
  • 结语

    spring的@Async真的极大的方便了java的异步(多线程)开发,心里默念三遍:感谢spring!感谢spring!感谢spring!

Spring及SpringBoot @Async配置步骤及注意事项的更多相关文章

  1. SpringBoot线程池的创建、@Async配置步骤及注意事项

    最近在做订单模块,用户购买服务类产品之后,需要进行预约,预约成功之后分别给商家和用户发送提醒短信.考虑发短信耗时的情况所以我想用异步的方法去执行,于是就在网上看见了Spring的@Async了. 但是 ...

  2. IntelliJ IDEA 2017 MySQL5 绿色版 Spring 4 Mybatis 3 配置步骤详解(二)

    前言    继续上一篇安装教程 首先是MySQL绿色版安装之后其他组件安装,如果篇幅较长会分为多篇深入讲解,随笔属于学习笔记诸多错误还望指出 共同学习. MySQL 5.7 绿色版   我本地安装的是 ...

  3. SpringBoot自定义配置步骤

    1. 在yml中填写自定义配置 ly: sms: accessKeyId: # 短信配置 accessKeySecret: signName: xx商城 # 签名名称 verifyCodeTempla ...

  4. 整合struts2+hibernate详细配置步骤及注意事项

    刚刚学完这两个框架,就迫不及待的做了一个例子,在整合两个框架的时候,也碰到了一些小问题,下面介绍一下配置的步骤: 1.创建一个自定义的struts2和hibernate的类库 因为之前写例子都是直接将 ...

  5. spring-boot @Async 的使用、自定义Executor的配置方法

    1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncT ...

  6. spring boot使用自定义配置的线程池执行Async异步任务

    一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context ...

  7. SpringBoot 自动配置:Spring Data JPA

    前言 不知道从啥时候开始项目上就一直用MyBatis,其实我个人更新JPA些,因为JPA看起来OO的思想更强烈些,所以这才最近把JPA拿出来再看一看,使用起来也很简单,除了定义Entity实体外,声明 ...

  8. spring基于xml的声明式事务控制配置步骤

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  9. SpringBoot 优雅配置跨域多种方式及Spring Security跨域访问配置的坑

    前言 最近在做项目的时候,基于前后端分离的权限管理系统,后台使用 Spring Security 作为权限控制管理, 然后在前端接口访问时候涉及到跨域,但我怎么配置跨域也没有生效,这里有一个坑,在使用 ...

随机推荐

  1. net core体系-Standard-1概述

    前言 早上起来.NET社区沸腾了,期待已久的.NET Core 2.0终于发布!根据个人经验,微软的产品一般在2.0时会趋于成熟,所以一个新的.Net开发时代已经来临!未来属于.NET Core. . ...

  2. python第十五天

    什么是模块? 一系列功能的集合 定义模块? 创建一个py文件就是一个模块,该py文件名就是模块名 怎么使用模块? 在要是用的模块文件中通过import 模块名 来导入模块 模块的四种方式? 1.编译执 ...

  3. 了解 ptyhon垃圾回收机制

    Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的 ...

  4. lsblk

    linux磁盘命令-lsblk显现磁盘阵列分组 lsblk(list block devices)能列出系统上所有的磁盘. lsblk [-dfimpt] [device] 选项与参数: -d :仅列 ...

  5. UOJ.311.[UNR#2]积劳成疾(DP)

    UOJ 序列中的每个位置是等价的.直接令\(f[i][j]\)表示,\(i\)个数的序列,最大值不超过\(j\)的所有序列每个长为\(k\)的子区间最大值的乘积的和. 由\(j-1\)转移到\(j\) ...

  6. Android中的数据持久化机制

    Android中几种最简单但是却最通用的数据持久化技术:SharedPreference.实例状态Bundle和本地文件. Android的非确定性Activity和应用程序生存期使在会话间保留UI状 ...

  7. Bphero-UWB 基站0 和 电脑串口数据格式定义

    基站0 通过串口将系统中测得的距离信息发送到电脑,电脑定位软件通过三边定位算法计算出TAG的坐标,基站0 和 定位软件之间的数据格式定义如下(对官方数据结构进行了简化) 更多UWB定位信息请参阅论坛b ...

  8. tensorflow 使用 3 模型学习

    模型学习 import tensorflow as tf import numpy as np # 生成 100 个随机的点 x_data = np.random.rand( 100 ) y_data ...

  9. Tensor索引操作

    #Tensor索引操作 ''''' Tensor支持与numpy.ndarray类似的索引操作,语法上也类似 如无特殊说明,索引出来的结果与原tensor共享内存,即修改一个,另一个会跟着修改 ''' ...

  10. 解决微信浏览器中无法一键拨号问题tel

    公众号中需要在某些页面显示手机号码,并且需要点击后拨号. 原以为 <a href="tel:10086">10086</a> 可以解决了, 没想到在微信浏览 ...