Spring Boot异步线程间数据传递的四种方式

目录
  • Spring Boot 自定义线程池实现异步开发
  • 1. 手动设置
  • 2. 线程池设置TaskDecorator
  • 3. InheritableThreadLocal
  • 4. TransmittableThreadLocal
    • TransmittableThreadLocal原理
  • 总结

Spring Boot 自定义线程池实现异步开发

Spring Boot 自定义线程池实现异步开发相信看过的都了解,但是在实际开发中需要在父子线程之间传递一些数据,比如用户信息,链路信息等等

比如用户登录信息使用ThreadLocal存放保证线程隔离,代码如下:

/**
 * @author 公众号:码猿技术专栏
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final  ThreadLocal<LoginVal> loginValThreadLocal=new ThreadLocal<>();
    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}

那么子线程想要获取这个LoginVal如何做呢?

今天就来介绍几种优雅的方式实现Spring Boot 内部的父子线程的数据传递。

1. 手动设置

每执行一次异步线程都要分为两步:

  • 获取父线程的LoginVal
  • 将LoginVal设置到子线程,达到复用

代码如下:

public void handlerAsync() {
        //1. 获取父线程的loginVal
        LoginVal loginVal = OauthContext.get();
        log.info("父线程的值:{}",OauthContext.get());
        CompletableFuture.runAsync(()->{
            //2. 设置子线程的值,复用
           OauthContext.set(loginVal);
           log.info("子线程的值:{}",OauthContext.get());
        });
    }

虽然能够实现目的,但是每次开异步线程都需要手动设置,重复代码太多,看了头疼,你认为优雅吗?

2. 线程池设置TaskDecorator

TaskDecorator是什么?官方api的大致意思:这是一个执行回调方法的装饰器,主要应用于传递上下文,或者提供任务的监控/统计信息。

知道有这么一个东西,如何去使用?

TaskDecorator是一个接口,首先需要去实现它,代码如下:

/**
 * @description 上下文装饰器
 */
public class ContextTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        //获取父线程的loginVal
        LoginVal loginVal = OauthContext.get();
        return () -> {
            try {
                // 将主线程的请求信息,设置到子线程中
                OauthContext.set(loginVal);
                // 执行子线程,这一步不要忘了
                runnable.run();
            } finally {
                // 线程结束,清空这些信息,否则可能造成内存泄漏
                OauthContext.clear();
            }
        };
    }
}

这里我只是设置了LoginVal,实际开发中其他的共享数据,比如SecurityContextRequestAttributes....

TaskDecorator需要结合线程池使用,实际开发中异步线程建议使用线程池,只需要在对应的线程池配置一下,代码如下:

@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();
        poolTaskExecutor.setCorePoolSize(xx);
        poolTaskExecutor.setMaxPoolSize(xx);
        // 设置线程活跃时间(秒)
        poolTaskExecutor.setKeepAliveSeconds(xx);
        // 设置队列容量
        poolTaskExecutor.setQueueCapacity(xx);
        //设置TaskDecorator,用于解决父子线程间的数据复用
        poolTaskExecutor.setTaskDecorator(new ContextTaskDecorator());
        poolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        poolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        return poolTaskExecutor;
    }

此时业务代码就不需要去设置子线程的值,直接使用即可,代码如下:

public void handlerAsync() {
        log.info("父线程的用户信息:{}", OauthContext.get());
        //执行异步任务,需要指定的线程池
        CompletableFuture.runAsync(()-&gt; log.info("子线程的用户信息:{}", OauthContext.get()),taskExecutor);
    }

来看一下结果,如下图:

这里使用的是CompletableFuture执行异步任务,使用@Async这个注解同样是可行的。

注意:无论使用何种方式,都需要指定线程池

3. InheritableThreadLocal

这种方案不建议使用,InheritableThreadLocal虽然能够实现父子线程间的复用,但是在线程池中使用会存在复用的问题,具体的可以看陈某之前的文章:微服务中使用阿里开源的TTL,优雅的实现身份信息的线程间复用

这种方案使用也是非常简单,直接用InheritableThreadLocal替换ThreadLocal即可,代码如下:

/**
 *
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final  InheritableThreadLocal<LoginVal> loginValThreadLocal=new InheritableThreadLocal<>();
    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}

4. TransmittableThreadLocal

TransmittableThreadLocal是阿里开源的工具,弥补了InheritableThreadLocal的缺陷,在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。

使用起来也是非常简单,添加依赖如下:

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>transmittable-thread-local</artifactId>
	<version>2.14.2</version>
</dependency>

OauthContext改造代码如下:

/**
 * @description 用户上下文信息
 */
public class OauthContext {
    private static  final TransmittableThreadLocal<LoginVal> loginValThreadLocal=new TransmittableThreadLocal<>();
    public static  LoginVal get(){
        return loginValThreadLocal.get();
    }
    public static void set(LoginVal loginVal){
        loginValThreadLocal.set(loginVal);
    }
    public static void clear(){
        loginValThreadLocal.remove();
    }
}

关于TransmittableThreadLocal想深入了解其原理可以看陈某之前的文章:微服务中使用阿里开源的TTL,优雅的实现身份信息的线程间复用,应用还是非常广泛的

TransmittableThreadLocal原理

从定义来看,TransimittableThreadLocal继承于InheritableThreadLocal,并实现TtlCopier接口,它里面只有一个copy方法。所以主要是对InheritableThreadLocal的扩展。

public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> implements TtlCopier<T>

TransimittableThreadLocal中添加holder属性。这个属性的作用就是被标记为具备线程传递资格的对象都会被添加到这个对象中。

要标记一个类,比较容易想到的方式,就是给这个类新增一个Type字段,还有一个方法就是将具备这种类型的的对象都添加到一个静态全局集合中。之后使用时,这个集合里的所有值都具备这个标记。

// 1. holder本身是一个InheritableThreadLocal对象
// 2. 这个holder对象的value是WeakHashMap<TransmittableThreadLocal<Object>, ?>
//   2.1 WeekHashMap的value总是null,且不可能被使用。
//    2.2 WeekHasshMap支持value=null
private static InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder = new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
  @Override
  protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
    return new WeakHashMap<TransmittableThreadLocal<Object>, Object>();
  }
  /**
   * 重写了childValue方法,实现上直接将父线程的属性作为子线程的本地变量对象。
   */
  @Override
  protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
    return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);
  }
};

应用代码是通过TtlExecutors工具类对线程池对象进行包装。工具类只是简单的判断,输入的线程池是否已经被包装过、非空校验等,然后返回包装类ExecutorServiceTtlWrapper。根据不同的线程池类型,有不同和的包装类。

@Nullable
public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {
  if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) {
    return executorService;
  }
  return new ExecutorServiceTtlWrapper(executorService);
}

进入包装类ExecutorServiceTtlWrapper。可以注意到不论是通过ExecutorServiceTtlWrapper#submit方法或者是ExecutorTtlWrapper#execute方法,都会将线程对象包装成TtlCallable或者TtlRunnable,用于在真正执行run方法前做一些业务逻辑。

/**
 * 在ExecutorServiceTtlWrapper实现submit方法
 */
@NonNull
@Override
public <T> Future<T> submit(@NonNull Callable<T> task) {
  return executorService.submit(TtlCallable.get(task));
}
/**
 * 在ExecutorTtlWrapper实现execute方法
 */
@Override
public void execute(@NonNull Runnable command) {
  executor.execute(TtlRunnable.get(command));
}

所以,重点的核心逻辑应该是在TtlCallable#call()或者TtlRunnable#run()中。以下以TtlCallable为例,TtlRunnable同理类似。在分析call()方法之前,先看一个类Transmitter

public static class Transmitter {
  /**
    * 捕获当前线程中的是所有TransimittableThreadLocal和注册ThreadLocal的值。
    */
  @NonNull
  public static Object capture() {
    return new Snapshot(captureTtlValues(), captureThreadLocalValues());
  }
    /**
    * 捕获TransimittableThreadLocal的值,将holder中的所有值都添加到HashMap后返回。
    */
  private static HashMap<TransmittableThreadLocal<Object>, Object> captureTtlValues() {
    HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value =
      new HashMap<TransmittableThreadLocal<Object>, Object>();
    for (TransmittableThreadLocal<Object> threadLocal : holder.get().keySet()) {
      ttl2Value.put(threadLocal, threadLocal.copyValue());
    }
    return ttl2Value;
  }
  /**
    * 捕获注册的ThreadLocal的值,也就是原本线程中的ThreadLocal,可以注册到TTL中,在
    * 进行线程池本地变量传递时也会被传递。
    */
  private static HashMap<ThreadLocal<Object>, Object> captureThreadLocalValues() {
    final HashMap<ThreadLocal<Object>, Object> threadLocal2Value =
      new HashMap<ThreadLocal<Object>, Object>();
    for(Map.Entry<ThreadLocal<Object>,TtlCopier<Object>>entry:threadLocalHolder.entrySet()){
      final ThreadLocal<Object> threadLocal = entry.getKey();
      final TtlCopier<Object> copier = entry.getValue();
      threadLocal2Value.put(threadLocal, copier.copy(threadLocal.get()));
    }
    return threadLocal2Value;
  }
  /**
    * 将捕获到的本地变量进行替换子线程的本地变量,并且返回子线程现有的本地变量副本backup。
    * 用于在执行run/call方法之后,将本地变量副本恢复。
    */
  @NonNull
  public static Object replay(@NonNull Object captured) {
    final Snapshot capturedSnapshot = (Snapshot) captured;
    return new Snapshot(replayTtlValues(capturedSnapshot.ttl2Value),
                        replayThreadLocalValues(capturedSnapshot.threadLocal2Value));
  }
  /**
    * 替换TransmittableThreadLocal
    */
  @NonNull
  private static HashMap<TransmittableThreadLocal<Object>, Object> replayTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> captured) {
    // 创建副本backup
    HashMap<TransmittableThreadLocal<Object>, Object> backup =
      new HashMap<TransmittableThreadLocal<Object>, Object>();
    for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
      TransmittableThreadLocal<Object> threadLocal = iterator.next();
      // 对当前线程的本地变量进行副本拷贝
      backup.put(threadLocal, threadLocal.get());
      // 若出现调用线程中不存在某个线程变量,而线程池中线程有,则删除线程池中对应的本地变量
      if (!captured.containsKey(threadLocal)) {
        iterator.remove();
        threadLocal.superRemove();
      }
    }
    // 将捕获的TTL值打入线程池获取到的线程TTL中。
    setTtlValuesTo(captured);
    // 是一个扩展点,调用TTL的beforeExecute方法。默认实现为空
    doExecuteCallback(true);
    return backup;
  }
  private static HashMap<ThreadLocal<Object>, Object> replayThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> captured) {
    final HashMap<ThreadLocal<Object>, Object> backup =
      new HashMap<ThreadLocal<Object>, Object>();
    for (Map.Entry<ThreadLocal<Object>, Object> entry : captured.entrySet()) {
      final ThreadLocal<Object> threadLocal = entry.getKey();
      backup.put(threadLocal, threadLocal.get());
      final Object value = entry.getValue();
      if (value == threadLocalClearMark) threadLocal.remove();
      else threadLocal.set(value);
    }
    return backup;
  }
  /**
    * 清除单线线程的所有TTL和TL,并返回清除之气的backup
    */
  @NonNull
  public static Object clear() {
    final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value =
      new HashMap<TransmittableThreadLocal<Object>, Object>();
    final HashMap<ThreadLocal<Object>, Object> threadLocal2Value =
      new HashMap<ThreadLocal<Object>, Object>();
    for(Map.Entry<ThreadLocal<Object>,TtlCopier<Object>>entry:threadLocalHolder.entrySet()){
      final ThreadLocal<Object> threadLocal = entry.getKey();
      threadLocal2Value.put(threadLocal, threadLocalClearMark);
    }
    return replay(new Snapshot(ttl2Value, threadLocal2Value));
  }
  /**
    * 还原
    */
  public static void restore(@NonNull Object backup) {
    final Snapshot backupSnapshot = (Snapshot) backup;
    restoreTtlValues(backupSnapshot.ttl2Value);
    restoreThreadLocalValues(backupSnapshot.threadLocal2Value);
  }
  private static void restoreTtlValues(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> backup) {
    // 扩展点,调用TTL的afterExecute
    doExecuteCallback(false);
    for (final Iterator<TransmittableThreadLocal<Object>> iterator = holder.get().keySet().iterator(); iterator.hasNext(); ) {
      TransmittableThreadLocal<Object> threadLocal = iterator.next();
      if (!backup.containsKey(threadLocal)) {
        iterator.remove();
        threadLocal.superRemove();
      }
    }
    // 将本地变量恢复成备份版本
    setTtlValuesTo(backup);
  }
  private static void setTtlValuesTo(@NonNull HashMap<TransmittableThreadLocal<Object>, Object> ttlValues) {
    for (Map.Entry<TransmittableThreadLocal<Object>, Object> entry : ttlValues.entrySet()) {
      TransmittableThreadLocal<Object> threadLocal = entry.getKey();
      threadLocal.set(entry.getValue());
    }
  }
  private static void restoreThreadLocalValues(@NonNull HashMap<ThreadLocal<Object>, Object> backup) {
    for (Map.Entry<ThreadLocal<Object>, Object> entry : backup.entrySet()) {
      final ThreadLocal<Object> threadLocal = entry.getKey();
      threadLocal.set(entry.getValue());
    }
  }
  /**
   * 快照类,保存TTL和TL
   */
  private static class Snapshot {
    final HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value;
    final HashMap<ThreadLocal<Object>, Object> threadLocal2Value;
    private Snapshot(HashMap<TransmittableThreadLocal<Object>, Object> ttl2Value,
                     HashMap<ThreadLocal<Object>, Object> threadLocal2Value) {
      this.ttl2Value = ttl2Value;
      this.threadLocal2Value = threadLocal2Value;
    }
  }

进入TtlCallable#call()方法。

@Override
public V call() throws Exception {
  Object captured = capturedRef.get();
  if (captured == null || releaseTtlValueReferenceAfterCall &amp;&amp;
      !capturedRef.compareAndSet(captured, null)) {
    throw new IllegalStateException("TTL value reference is released after call!");
  }
  // 调用replay方法将捕获到的当前线程的本地变量,传递给线程池线程的本地变量,
  // 并且获取到线程池线程覆盖之前的本地变量副本。
  Object backup = replay(captured);
  try {
    // 线程方法调用
    return callable.call();
  } finally {
    // 使用副本进行恢复。
    restore(backup);
  }
}

到这基本上线程池方式传递本地变量的核心代码已经大概看完了。总的来说在创建TtlCallable对象是,调用capture()方法捕获调用方的本地线程变量,在call()执行时,将捕获到的线程变量,替换到线程池所对应获取到的线程的本地变量中,并且在执行完成之后,将其本地变量恢复到调用之前。

总结

上述列举了4种方案,陈某这里推荐方案2和方案4,其中两种方案的缺点非常明显,实际开发中也是采用的方案2或者方案4

以上就是Spring Boot异步线程间数据传递的四种方式的详细内容,更多关于Spring Boot异步线程数据传递的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot 自定义注解异步记录复杂日志详解

    目录 1.背景 2.技术方案-自定义注解 2.1 注解介绍 2.2 元注解 2.3 实现自定义注解 3.技术方案-AOP切面 3.1 AOP术语解析 3.2 切入点表达式 3.3 ADVICE通知类型 3.4 技术实现 3.5 相关操作 4.高级操作 1.背景 最近接手一个任务,需要给当前项目加一个较为复杂的日志.有多复杂呢? 要有日志类型.不同日志类型要有不同的操作和备注等.作为小白的我最开始的做法是在业务层写代码记录日志,好处就是方便,坏处就是这种做法直接侵袭Service层,Service

  • 详细讲解springboot如何实现异步任务

    目录 Spring Boot介绍 Spring Boot特点 异步任务 Spring Boot介绍 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架. Spr

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

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

  • SpringBoot 异步线程间传递上下文方式

    目录 异步线程间传递上下文 需求 实现 启用异步功能 配置异步 配置任务装饰器 启用多线程安全上下文无法在线程间共享问题 问题 解决方案 原理 结果 异步线程间传递上下文 需求 SpringBoot项目中,经常使用@Async来开启一个子线程来完成异步操作.主线程中的用户信息需要传递给子线程 实现 启用异步功能 在启动类里加上@EnableAsync注解 @EnableAsync @SpringBootApplication public class Application {} 配置异步 新建

  • SpringBoot整合MQTT并实现异步线程调用的问题

    目录 为什么选择MQTT 使用背景 代码实现 基础代码 异步线程处理实现 为什么选择MQTT MQTT的定义相信很多人都能讲的头头是道,本文章也不讨论什么高大上的东西,旨在用最简单直观的方式让每一位刚接触的同行们可以最快的应用起来 先从使用MQTT需要什么开始分析: 消息服务器 不同应用/设备之间的频繁交互 可能涉及一对多的消息传递 基于SpringBoot通过注解实现对mqtt消息处理的异步调用 使用背景 生产环境下, 由于mqtt 生产者生产的消息逐渐增多, 可能会导致消息堆积. 因此需要消

  • SpringBoot多线程进行异步请求的处理方式

    目录 SpringBoot多线程进行异步请求的处理 第一步:编写配置类 第二步:对方法使用注解标注为使用多线程进行处理 SpringBoot请求线程优化 使用Callable来实现 1.异步调用的另一种方式 3.Deferred方式实现异步调用 SpringBoot多线程进行异步请求的处理 近期在协会博客园中,有人发布了博客,系统进行查重的时候由于机器最低配置进行大量计算时需要十秒左右时间才能处理完,由于一开始是单例模式,导致,在某人查重的时候整个系统是不会再响应别的请求的,导致了系统假死状态,

  • springboot利用aop实现接口异步(进度条)的全过程

    目录 一.前言 二.时序图 三.功能演示 四.关键代码 Controller AsyncAop AsyncService 五.源码地址 总结 一.前言 在项目中发现有接口(excel导入数据)处理数据需要耗时比较长的时间,是因为数据量比较大,同时数据的校验需要耗费一定时间,决定使用一种通用的方法解决这个问题. 解决方案:通过aop使接口异步处理,前端轮询另外一个接口查询进度. 目标: 1接口上一个注解即可实现接口异步(优化:可以通过header参数动态控制是否异步) 2一个方法实现进度条的更新

  • Spring Boot异步线程间数据传递的四种方式

    目录 Spring Boot 自定义线程池实现异步开发 1. 手动设置 2. 线程池设置TaskDecorator 3. InheritableThreadLocal 4. TransmittableThreadLocal TransmittableThreadLocal原理 总结 Spring Boot 自定义线程池实现异步开发 Spring Boot 自定义线程池实现异步开发相信看过的都了解,但是在实际开发中需要在父子线程之间传递一些数据,比如用户信息,链路信息等等 比如用户登录信息使用Th

  • Vue 父子组件数据传递的四种方式( inheritAttrs + $attrs + $listeners)

    当我们在书写 vue 组件的时候,也许可能会用到数据传递:将父组件的数据传递给子组件,有时候也需要通过子组件去事件去触发父组件的事件: 每当我们遇到这样的需求的时候,我们总是会想到有三种解决办法: 通过 props 的方式向子组件传递(父子组件) vuex 进行状态管理(父子组件和非父子组件) vuex 非父子组件的通信传递 Vue Event Bus ,使用Vue的实例,实现事件的监听和发布,实现组件之间的传递. 后来再逛社区的时候我又发现了还有第四种传递方式, inheritAttrs +

  • linux下实现web数据同步的四种方式(性能比较)

    实现web数据同步的四种方式 ======================================= 1.nfs实现web数据共享2.rsync +inotify实现web数据同步3.rsync+sersync更快更节约资源实现web数据同步4.unison+inotify实现web数据双向同步 ======================================= 一.nfs实现web数据共享 nfs能实现数据同步是通过NAS(网络附加存储),在服务器上共享一个文件,且服务器需

  • ASP.NET页面间数据传递的几种方法介绍

    Web页面是无状态的,服务器对每一次请求都认为来自不同用户,因此,变量的状态在连续对同一页面的多次请求之间或在页面跳转时不会被保留.在用ASP.NET 设计开发一个Web系统时, 遇到一个重要的问题是如何保证数据在页面间进行正确.安全和高效地传送,Asp.net 提供了状态管理等多种技术来解决保存和传递数据问题,以下来探讨.NET 下的解决此问题的各种方法和各自的适用场合. 一.ASP.NET页面间数据传递的各种方法和分析 1.使用Querystring 方法 QueryString 也叫查询字

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

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

  • Spring Boot使用线程池处理上万条数据插入功能

    目录 # 前言 # 使用步骤 # 前言 前两天做项目的时候,想提高一下插入表的性能优化,因为是两张表,先插旧的表,紧接着插新的表,一万多条数据就有点慢了 后面就想到了线程池ThreadPoolExecutor,而用的是Spring Boot项目,可以用Spring提供的对ThreadPoolExecutor封装的线程池ThreadPoolTaskExecutor,直接使用注解启用 # 使用步骤 先创建一个线程池的配置,让Spring Boot加载,用来定义如何创建一个ThreadPoolTask

  • 深入理解spring boot异步调用方式@Async

    本文主要给大家介绍了关于spring boot异步调用方式@Async的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 1.使用背景 在日常开发的项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行,我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async. 2.异步处理方式 调用之后,不返回任何数据. 调用之后,返回数据,通过Future来获取返回数据 3.@Async不返回数据 使用@EnableAsyn

  • 基于Spring Boot的线程池监控问题及解决方案

    目录 前言 为什么需要对线程池进行监控 如何做线程池的监控 数据采集 数据存储以及大盘的展示 进一步扩展以及思考 如何合理配置线程池参数 如何动态调整线程池参数 如何给不同的服务之间做线程池的隔离 实现方案 前言 这篇是推动大家异步编程的思想的线程池的准备篇,要做好监控,让大家使用无后顾之忧,敬畏生产. 为什么需要对线程池进行监控 Java线程池作为最常使用到的并发工具,相信大家都不陌生,但是你真的确定使用对了吗?大名鼎鼎的阿里Java代码规范要求我们不使用 Executors来快速创建线程池,

  • 详解Spring Boot 异步执行方法

    最近遇到一个需求,就是当服务器接到请求并不需要任务执行完成才返回结果,可以立即返回结果,让任务异步的去执行.开始考虑是直接启一个新的线程去执行任务或者把任务提交到一个线程池去执行,这两种方法都是可以的.但是 Spring 这么强大,肯定有什么更简单的方法,就 google 了一下,还真有呢.就是使用 @EnableAsync 和 @Async 这两个注解就 ok 了. 给方法加上 @Async 注解 package me.deweixu.aysncdemo.service; public int

随机推荐