浅谈Java中spring 线程异步执行

多线程并发处理起来通常比较麻烦,如果你使用spring容器来管理业务bean,事情就好办了多了。spring封装了Java的多线程的实现,你只需要关注于并发事物的流程以及一些并发负载量等特性,具体来说如何使用spring来处理并发事务:

1.了解 TaskExecutor接口

Spring的TaskExecutor接口等同于java.util.concurrent.Executor接口。 实际上,它存在的主要原因是为了在使用线程池的时候,将对Java5的依赖抽象出来。 这个接口只有一个方法execute(Runnable task),它根据线程池的语义和配置,来接受一个执行任务。最初创建TaskExecutor是为了在需要时给其他Spring组件提供一个线程池的抽象。 例如ApplicationEventMulticaster组件、JMS的 AbstractMessageListenerContainer和对Quartz的整合都使用了TaskExecutor抽象来提供线程池。 当然,如果你的bean需要线程池行为,你也可以使用这个抽象层。

2. TaskExecutor接口的实现类

(1)SimpleAsyncTaskExecutor 类

这个实现不重用任何线程,或者说它每次调用都启动一个新线程。但是,它还是支持对并发总数设限,当超过线程并发总数限制时,阻塞新的调用,直到有位置被释放。如果你需要真正的池,请继续往下看。

(2)SyncTaskExecutor类

这个实现不会异步执行。相反,每次调用都在发起调用的线程中执行。它的主要用处是在不需要多线程的时候,比如简单的test case。

(3)ConcurrentTaskExecutor 类

这个实现是对Java 5 java.util.concurrent.Executor类的包装。有另一个备选, ThreadPoolTaskExecutor类,它暴露了Executor的配置参数作为bean属性。很少需要使用ConcurrentTaskExecutor, 但是如果ThreadPoolTaskExecutor不敷所需,ConcurrentTaskExecutor是另外一个备选。

(4)SimpleThreadPoolTaskExecutor 类

这个实现实际上是Quartz的SimpleThreadPool类的子类,它会监听Spring的生命周期回调。当你有线程池,需要在Quartz和非Quartz组件中共用时,这是它的典型用处。

(5)ThreadPoolTaskExecutor 类

它不支持任何对java.util.concurrent包的替换或者下行移植。Doug Lea和Dawid Kurzyniec对java.util.concurrent的实现都采用了不同的包结构,导致它们无法正确运行。 这个实现只能在Java 5环境中使用,但是却是这个环境中最常用的。它暴露的bean properties可以用来配置一个java.util.concurrent.ThreadPoolExecutor,把它包装到一个TaskExecutor中。如果你需要更加先进的类,比如ScheduledThreadPoolExecutor,我们建议你使用ConcurrentTaskExecutor来替代。

(6)TimerTaskExecutor类

这个实现使用一个TimerTask作为其背后的实现。它和SyncTaskExecutor的不同在于,方法调用是在一个独立的线程中进行的,虽然在那个线程中是同步的。

(7)WorkManagerTaskExecutor类

这个实现使用了CommonJ WorkManager作为其底层实现,是在Spring context中配置CommonJ WorkManager应用的最重要的类。和SimpleThreadPoolTaskExecutor类似,这个类实现了WorkManager接口,因此可以直接作为WorkManager使用。

案例

注册TaskExecutor

@Configuration
public class WebMvcConfigurerAdpter extends AbstractWebMvcConfigurerAdpter {

 @Override
 public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  super.configureMessageConverters(converters);
  WafJsonMapper.getMapper().enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
 }

 @Bean
 public TaskExecutor taskExecutor() {
  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(5);
  executor.setMaxPoolSize(10);
  return executor;
 }
}

使用:

@Service
public class TaskService {

 @Autowired
 private TaskExecutor executor;

 public void execute() {
  executor.execute(new Runnable() {
   @Override
   public void run() {
    for (int i = 0; i < 10; i++) {
     try {
      Thread.sleep(1000);
      System.out.println("task running ...");
     } catch (Exception e) {

     }
    }
   }
  });
 }
}
@RestController
@RequestMapping(value = "/v0.1")
public class TaskController {

 @Autowired
 private TaskService taskService;

 @RequestMapping()
 public Object execute() {
  taskService.execute();
  Map res = new HashMap();
  res.put("result", "success");
  return res;
 }
}

程序不会等到10个线程都跑完才返回结果,不是阻塞程序,返回结果后,线程仍然在执行。

案例:

ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池所使用的缓冲队列
poolTaskExecutor.setQueueCapacity(200);
//线程池维护线程的最少数量
poolTaskExecutor.setCorePoolSize(5);
//线程池维护线程的最大数量
poolTaskExecutor.setMaxPoolSize(1000);
//线程池维护线程所允许的空闲时间
poolTaskExecutor.setKeepAliveSeconds(30000);
poolTaskExecutor.initialize();
<!-- 配置线程池 -->
<bean id ="taskExecutor" class ="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
 <!-- 线程池维护线程的最少数量 -->
<span style="white-space:pre"> </span><property name ="corePoolSize" value ="5" />
 <!-- 线程池维护线程所允许的空闲时间 -->
<span style="white-space:pre"> </span><property name ="keepAliveSeconds" value ="30000" />
 <!-- 线程池维护线程的最大数量 -->
<span style="white-space:pre"> </span><property name ="maxPoolSize" value ="1000" />
 <!-- 线程池所使用的缓冲队列 -->
<span style="white-space:pre"> </span><property name ="queueCapacity" value ="200" />
</bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ThreadPoolTaskExecutor poolTaskExecutor = (ThreadPoolTaskExecutor)ctx.getBean("taskExecutor");

Thread udpThread = new Thread(udp);
poolTaskExecutor.execute(udpThread);
获取当前线程池活动的线程数:
int count = poolTaskExecutor.getActiveCount();
logger.debug("[x] - now threadpool active threads totalNum : " +count);

配置解释

当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

到此这篇关于浅谈spring 线程异步执行的文章就介绍到这了,更多相关spring 线程异步执行内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Boot利用@Async如何实现异步调用:自定义线程池

    前言 在之前的Spring Boot基础教程系列中,已经通过<Spring Boot中使用@Async实现异步调用>一文介绍过如何使用@Async注解来实现异步调用了.但是,对于这些异步执行的控制是我们保障自身应用健康的基本技能.本文我们就来学习一下,如果通过自定义线程池的方式来控制异步调用的并发. 本文中的例子我们可以在之前的例子基础上修改,也可以创建一个全新的Spring Boot项目来尝试. 定义线程池 第一步,先在Spring Boot主类中定义一个线程池,比如: @SpringBoo

  • spring异步service中处理线程数限制详解

    情况简介 spring项目,controller异步调用service的方法,产生大量并发. 具体业务: 前台同时传入大量待翻译的单词,后台业务接收单词,并调用百度翻译接口翻译接收单词并将翻译结果保存到数据库,前台不需要实时返回翻译结果. 处理方式: controller接收文本调用service中的异步方法,将单词先保存到队列中,再启动2个新线程,从缓存队列中取单词,并调用百度翻译接口获取翻译结果并将翻译结果保存到数据库. 本文主要知识点: 多线程同时(异步)调用方法后,开启新线程,并限制线程

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

    在前面的博客中,http://www.jb51.net/article/106718.htm 我们使用了spring boot的异步操作,当时,我们使用的是默认的线程池,但是,如果我们想根据项目来定制自己的线程池了,下面就来说说,如何定制线程池! 一.增加配置属性类 package com.chhliu.springboot.async.configuration; import org.springframework.boot.context.properties.ConfigurationP

  • 浅谈Spring @Async异步线程池用法总结

    本文介绍了Spring @Async异步线程池用法总结,分享给大家,希望对大家有帮助 1. TaskExecutor spring异步线程池的接口类,其实质是Java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程. 2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作.只适用于不需要多线程的地方 3. Conc

  • Spring Boot利用@Async异步调用:ThreadPoolTaskScheduler线程池的优雅关闭详解

    前言 之前分享了一篇关于Spring Boot中使用@Async来实现异步任务和线程池控制的文章:<Spring Boot使用@Async实现异步调用:自定义线程池>.由于最近身边也发现了不少异步任务没有正确处理而导致的不少问题,所以在本文就接前面内容,继续说说线程池的优雅关闭,主要针对ThreadPoolTaskScheduler线程池. 问题现象 在上篇文章的例子Chapter4-1-3中,我们定义了一个线程池,然后利用@Async注解写了3个任务,并指定了这些任务执行使用的线程池.在上文

  • Spring Boot使用Spring的异步线程池的实现

    前言 线程池,从名字上来看,就是一个保存线程的"池子",凡事都有其道理,那线程池的好处在哪里呢? 我们要让计算机为我们干一些活,其实都是在使用线程,使用方法就是new一个Runnable接口或者新建一个子类,继承于Thread类,这就会涉及到线程对象的创建与销毁,这两个操作无疑是耗费我们系统处理器资源的,那如何解决这个问题呢? 线程池其实就是为了解决这个问题而生的. 线程池提供了处理系统性能和大用户量请求之间的矛盾的方法,通过对多个任务重用已经存在的线程对象,降低了对线程对象创建和销毁

  • 详解Spring框架下向异步线程传递HttpServletRequest参数的坑

    在spring的注解 @RequestMapping 之下可以直接获取 HttpServletRequest 来获得诸如request header等重要的请求信息: @Slf4j @RestController @RequestMapping("/test") public class TestController { private static final String HEADER = "app-version"; @RequestMapping(value

  • 浅谈Java中spring 线程异步执行

    多线程并发处理起来通常比较麻烦,如果你使用spring容器来管理业务bean,事情就好办了多了.spring封装了Java的多线程的实现,你只需要关注于并发事物的流程以及一些并发负载量等特性,具体来说如何使用spring来处理并发事务: 1.了解 TaskExecutor接口 Spring的TaskExecutor接口等同于java.util.concurrent.Executor接口. 实际上,它存在的主要原因是为了在使用线程池的时候,将对Java5的依赖抽象出来. 这个接口只有一个方法exe

  • 浅谈Java中ArrayList线程不安全怎么办

    ArrayList线程不安全怎么办? 有三种解决方法: 使用对应的 Vector 类,这个类中的所有方法都加上了 synchronized 关键字 就和 HashMap 和 HashTable 的关系一样 使用 Collections 提供的 synchronizedList 方法,将一个原本线程不安全的集合类转换为线程安全的,使用方法如下: List<Integer> list = Collections.synchronizedList(new ArrayList<>());

  • 浅谈java中守护线程与用户线程

    Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程是一个用户线程,这个是我们可以看到的线程,其实JVM内部同时还启动了好多守护线程,比如垃圾回收线程.那么守护线程和用户线程有什么区别那?区别之一是当最后一个非守护线程结束时候,JVM会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出.言外之意是只要有一个用户线程还没结束正常情况下JVM就不会退出. 那么Java中如何创建一个守

  • 浅谈Java中Spring Boot的优势

    Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. Spring Boot 简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的.产品级别的 Spring 应用. 作为一名 Java 程序员如果你已经厌恶了传统的开发模式,希望有一个全新的框架可以提供快速开发,简单集成的编程体验,强烈建议你学习了解 Spring Boot .

  • 浅谈java中异步多线程超时导致的服务异常

    在项目中为了提高大并发量时的性能稳定性,经常会使用到线程池来做多线程异步操作,多线程有2种,一种是实现runnable接口,这种没有返回值,一种是实现Callable接口,这种有返回值. 当其中一个线程超时的时候,理论上应该不 影响其他线程的执行结果,但是在项目中出现的问题表明一个线程阻塞,其他线程返回的接口都为空.其实是个很简单的问题,但是由于第一次碰到,还是想了一些时间的.很简单,就是因为阻塞的那个线 程没有释放,并发量一大,线程池数量就满了,所以其他线程都处于等待状态. 附上一段自己写的调

  • 浅谈Java中ABA问题及避免

    本文主要研究的是关于Java中ABA问题及避免的相关内容,具体如下. 在<Java并发实战>一书的第15章中有一个用原子变量实现的并发栈,代码如下: public class Node { public final String item; public Node next; public Node(String item){ this.item = item; } } public class ConcurrentStack { AtomicReference<Node> top

  • 浅谈Java中的atomic包实现原理及应用

    1.同步问题的提出 假设我们使用一个双核处理器执行A和B两个线程,核1执行A线程,而核2执行B线程,这两个线程现在都要对名为obj的对象的成员变量i进行加1操作,假设i的初始值为0,理论上两个线程运行后i的值应该变成2,但实际上很有可能结果为1. 我们现在来分析原因,这里为了分析的简单,我们不考虑缓存的情况,实际上有缓存会使结果为1的可能性增大.A线程将内存中的变量i读取到核1算数运算单元中,然后进行加1操作,再将这个计算结果写回到内存中,因为上述操作不是原子操作,只要B线程在A线程将i增加1的

  • 浅谈Java中IO和NIO的本质和区别

    IO的本质 IO的作用就是从外部系统读取数据到java程序中,或者把java程序中输出的数据写回到外部系统.这里的外部系统可能是磁盘,网络流等等. 因为对所有的外部数据的处理都是由操作系统内核来实现的,对于java应用程序来说,只是调用操作系统中相应的接口方法,从而和外部数据进行交互. 所有IO的本质就是对Buffer的处理,我们把数据放入Buffer供系统写入外部数据,或者从系统Buffer中读取从外部系统中读取的数据.如下图所示: 用户空间也就是我们自己的java程序有一个Buffer,系统

  • 浅谈Java中的桥接方法与泛型的逆变和协变

    目录 1. 泛型的协变 1.1 泛型协变的使用 1.2 泛型协变存在的问题 1.2.1 Java当中桥接方法的来由 1.2.2 为什么泛型协变时,不允许添加元素呢 1.2.3 从Java字节码的角度去看桥接方法 2. 泛型逆变 2.1 泛型逆变的使用 2.2 泛型逆变会有什么问题 3.协变与逆变-PECS原则 泛型的协变和逆变是什么?对应于Java当中,协变对应的就是<? extends XXX>,而逆变对应的就是<? super XXX>. 1. 泛型的协变 1.1 泛型协变的使

  • 浅谈Java中Lock和Synchronized的区别

    目录 1. 从功能角度来看 2. 从特性来看 3. 从性能方面来看 1. 从功能角度来看 Lock和Synchronized都是java中去用来解决线程安全问题的一个工具 2. 从特性来看 Synchronized是java中的同步关键字,Lock是J.U.C包中提供的接口,而这个接口有很多的实现类,包括ReentrantLock这样重入锁的实现,Synchronized可以通过两种方式去控制锁的力度 一种把synchronized关键字修饰在方法层面,另一种是修饰在代码块上,可以通过synch

随机推荐