使用Async如何传递ThreadLocal及自定义线程池

问题:

        1. 当使用ThreadLocal存储数据时,程序中使用了@Async异步任务注解,后导致子任务中无法获取到ThreadLocal存储的数据

        2. 使用@Async注解后默认的线程池最大线程数和队列长度为 Integer.MAX_VALUE,当并发量增高后会导致系统负载飙升甚至系统宕机

方案一:

引入transmittable-thread-local依赖,依据此工具解决子线程获取ThreadLocal存储数据


  com.alibaba
  transmittable-thread-local
  2.12.3
  compile
public class MyThreadLocal {

  private static final ThreadLocal> param = new TransmittableThreadLocal<>();
...
}

这样就能解决子线程中无法获取父线程中ThreadLocal值的问题,使用方法简单,改造工作量很小,然后创建自定义线程池

@Configuration
public class AsyncThreadPoolConfig implements AsyncConfigurer {

  private static final String CUSTOMIZE_THREAD_POOL_PREFIX = "custom_thread_pool_";

  @Override
//  @Bean(name = "a1") 如果要定义多个,在@Async("a1")注解上表明使用哪个就可以了
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

    threadPoolTaskExecutor.setCorePoolSize(2);
    threadPoolTaskExecutor.setMaxPoolSize(100);
    threadPoolTaskExecutor.setThreadNamePrefix(CUSTOMIZE_THREAD_POOL_PREFIX);
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    threadPoolTaskExecutor.setQueueCapacity(200);
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    threadPoolTaskExecutor.initialize();

    return  TtlExecutors.getTtlExecutor(threadPoolTaskExecutor);
  }

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new SimpleAsyncUncaughtExceptionHandler();
  }
}

但是这个方案还是没办法解决MDC传递的问题

方案二:

不需要引入其他包,自定义线程池,实现ThreadLocal和MDC数据的传递

public class MyThreadLocal { private static final ThreadLocal> param = new ThreadLocal<>();

...
}

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncThreadPoolConfig implements AsyncConfigurer {

  private static final String CUSTOMIZE_THREAD_POOL_PREFIX = "custom_thread_pool_";

  @Override
// @Bean(name = "a1") 如果要定义多个,在@Async("a1")注解上表明使用哪个就可以了 
  public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor threadPoolTaskExecutor = new CustomThreadPoolTaskExecutor();

    threadPoolTaskExecutor.setCorePoolSize(2);
    threadPoolTaskExecutor.setMaxPoolSize(100);
    threadPoolTaskExecutor.setThreadNamePrefix(CUSTOMIZE_THREAD_POOL_PREFIX);
    threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    threadPoolTaskExecutor.setQueueCapacity(200);
    threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    threadPoolTaskExecutor.initialize();

    return threadPoolTaskExecutor;
  }

  @Override
  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new SimpleAsyncUncaughtExceptionHandler();
  }

  private static class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    @Override
    public void execute(Runnable task) {
      super.execute(new AsyncThreadPoolConfig.Runner(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }

    @Override
    public Future submit(Runnable task) {
      return super.submit(new AsyncThreadPoolConfig.Runner(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }

    @Override
    public  @NotNull Future submit(Callable task) {
      return super.submit(new AsyncThreadPoolConfig.Caller(task, MyThreadLocal.getParam(),
          MDC.getCopyOfContextMap(), Thread.currentThread()));
    }
  }

  @Slf4j
  private static class Runner implements Runnable {

    private final Thread parentThread;
    private final Runnable runnable;
    private final Map threadLocalContext;
    private final Map mdcContextMap;

    public Runner(Runnable runnable, Map threadLocalContext,
        Map mdcContextMap, Thread parentThread) {
      this.runnable = runnable;
      this.mdcContextMap = mdcContextMap;
      this.threadLocalContext = threadLocalContext;
      this.parentThread = parentThread;
    }

    @Override
    public void run() {
      //这里是子线程
      MyThreadLocal.setParam(threadLocalContext);
      MDC.setContextMap(mdcContextMap);
      try {
        runnable.run();
      } catch (Exception e) {
        log.error("异步任务异常 {}", e.getMessage());
        throw e;
      } finally {
        if (parentThread != Thread.currentThread()) {
          MyThreadLocal.removeParam();
          MDC.clear();
        }
      }
    }
  }

  @Slf4j
  private static class Caller implements Callable {

    private final Thread parentThread;
    private final Callable runnable;
    private final Map threadLocalContext;
    private final Map mdcContextMap;

    public Caller(Callable runnable, Map threadLocalContext,
        Map mdcContextMap, Thread parentThread) {
      this.runnable = runnable;
      this.threadLocalContext = threadLocalContext;
      this.mdcContextMap = mdcContextMap;
      this.parentThread = parentThread;
    }

    @Override
    public V call() throws Exception {
      //这里是子线程
      MyThreadLocal.setParam(threadLocalContext);
      MDC.setContextMap(mdcContextMap);
      try {
        return runnable.call();
      } catch (Exception e) {
        log.error("异步任务异常 {}", e.getMessage());
        throw e;
      } finally {
        if (parentThread != Thread.currentThread()) {
          MyThreadLocal.removeParam();
          MDC.clear();
        }
      }
    }
  }
}

你可能感兴趣的:(spring,JAVA,ThreadLocal传递,MDC传递,Async上下文传递,异步任务自定义线程池,Async自定义线程池)