SpringBoot优雅使用线程池

49 篇文章 2 订阅
36 篇文章 1 订阅

SpringBoot优雅使用线程池

依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot</artifactId>
      <version>2.3.4.RELEASE</version>
</dependency>

配置文件

task.pool.enabled=true
task.pool.corePoolSize=20
task.pool.maxPoolSize=40
task.pool.keepAliveSeconds=300
task.pool.queueCapacity=-1
task.pool.threadNamePrefix=Execute-
task.pool.threadGroupName=Group-

配置文件Bean

import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Data;

@Data
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolProperties {

    /**
     * 线程池是否可用
     */
    private boolean enabled = true;
    
    /**
     * 核心线程数
     */
    private int corePoolSize = 20;

    /**
     * 最大线程数
     */
    private int maxPoolSize = 40;

    /**
     * 线程池维护线程所允许的空闲时间
     */
    private int keepAliveSeconds = 300;

    /**
     * 队列最大长度 
     */
    private int queueCapacity = -1;

    /**
     * 线程名前缀
     */
    private String threadNamePrefix = "Execute-";
    
    /**
     * 线程组名称
     */
    private String threadGroupName = "Group-";
}

线程池配置

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@EnableAsync(proxyTargetClass = true)
@Configuration
@EnableConfigurationProperties({TaskThreadPoolProperties.class}) // 携带注入TaskThreadPoolProperties
@ConditionalOnProperty(prefix = "task.pool", value = "enabled", matchIfMissing = true) // 配置enabled为true才注入bean
public class TaskThreadPoolConfig {

    @Autowired
    private TaskThreadPoolProperties properties;

    // 定义线程池
    @Bean
    public AsyncTaskExecutor taskThreadPool() {
        // 不做扩展直接使用ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 可跳过自定义扩展线程池
        // 线程池扩展VisiableThreadPoolTaskExecutor
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        // 核心线程池大小
        executor.setCorePoolSize(properties.getCorePoolSize());
        // 最大线程数
        executor.setMaxPoolSize(properties.getMaxPoolSize());
        // 队列容量
        executor.setQueueCapacity(properties.getQueueCapacity());
        // 活跃时间
        executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
        // 线程名字前缀
        executor.setThreadNamePrefix(properties.getThreadNamePrefix());
        // 线程组名称
        executor.setThreadGroupName(properties.getThreadGroupName());

        /*
         * setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务,JDK提供了几个策略,这里使用CallerRunsPolicy
         * CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

自定义扩展线程池

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
    
    private static final long serialVersionUID = -8735033356653081039L;

    // 多线程任务装饰器
    public VisiableThreadPoolTaskExecutor(){
        // 这里对线程任务执行前后进行包装
        setTaskDecorator(new MDCTaskDecorator());
    }

    // 线程执行任务过程时打印线程池日志
    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (null == threadPoolExecutor) {
            return;
        }

        log.info( "{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(), prefix, threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(), threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute(Runnable)");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute(Runnable, long)");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit(Runnable)");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit(Callable<T>)");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable(Runnable)");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable(Callable<T>)");
        return super.submitListenable(task);
    }
}

MDCTaskDecorator,对执行的工作线程任务进行包装,这里场景是初始化每个线程独有的变量上下文,原理是使用MDC的ThreadLocal

import org.apache.commons.collections4.MapUtils;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;

import java.util.Map;

public class MDCTaskDecorator implements TaskDecorator {
    // 包装完的线程任务
    @Override
    public Runnable decorate(Runnable runnable) {
        // web thread, 每个工作线程都单独copy一份上下文数据
        Map<String, String> contextMap = ContextUtil.getCopyOfContextMap();
        return () -> {
            // work thread
            try {
                if(MapUtils.isNotEmpty(contextMap)){
                    MDC.setContextMap(contextMap);
                }
                // 具体的业务执行
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

应用上下文工具类ContextUtil

import org.slf4j.MDC;
import com.alibaba.fastjson.JSONObject;
import java.util.Map;

/**
 * Description:上下文信息
 */
public class ContextUtil {

    private ContextUtil() {}

    public static final String USER_ID = "userId";
    public static final String CLIENT_IP = "clientIp";
    public static final String ACCESS_TOKEN = "accessToken";
    public static final String SYS_LANGUAGE = "sysLanguage";

    // 上下文json串
    public static final String CONTEXT = "relayContext";

    public static void setUserId(String userId) {
        MDC.put(USER_ID, userId);
    }
    public static void setClientIp(String clientIp) {
        MDC.put(CLIENT_IP, clientIp);
    }
    public static void setAccessToken(String accessToken) {
        MDC.put(ACCESS_TOKEN, accessToken);
    }
    public static void setSysLanguage(String sysLanguage) {MDC.put(SYS_LANGUAGE, sysLanguage);}

    public static void initContext(String context) {
        RelayContext vo = JSONObject.parseObject(context, RelayContext.class);
        initContext(vo);
    }

    public static void initContext(RelayContext vo) {
        MDC.put(USER_ID, vo.getUserId());
        MDC.put(CLIENT_IP, vo.getClientIp());
        MDC.put(ACCESS_TOKEN, vo.getAccessToken());
        MDC.put(SYS_LANGUAGE, vo.getSysLanguage());
        MDC.put(CONTEXT, JSONObject.toJSONString(vo));
    }

    public static String getUserId() {
        return MDC.get(USER_ID);
    }
    public static String getClientIp() {
        return MDC.get(CLIENT_IP);
    }
    public static String getAccessToken() {
        return MDC.get(ACCESS_TOKEN);
    }
    public static  String getSysLanguage(){
        return MDC.get(SYS_LANGUAGE);
    }
    public static String getContext() {
        return MDC.get(CONTEXT);
    }

    public static void resetContext() {
        RelayContext vo = new RelayContext();
        MDC.put(USER_ID, vo.getUserId());
        MDC.put(CLIENT_IP, vo.getClientIp());
        MDC.put(ACCESS_TOKEN, vo.getAccessToken());
        MDC.put(SYS_LANGUAGE, vo.getSysLanguage());
        MDC.put(CONTEXT, JSONObject.toJSONString(vo));
    }

    public static void clear() {
        MDC.clear();
    }

    public static Map<String,String> getCopyOfContextMap(){
        return MDC.getCopyOfContextMap();
    }

}

使用线程池

// 外部注入TaskSerivce,调用asyncInsertTable方法即可使用线程池异步处理
@Service
@Slf4j
public class TaskSerivceImpl implements TaskSerivce {
    
    @Override
    @Async("taskThreadPool")
    public void asyncInsertTable() {
        // do somethings
    }
    
}

// 注入线程池使用
@Autowired
@Qualifier("taskThreadPool")
private AsyncTaskExecutor executor;

executor.submit(() -> {
	// 逻辑
});

源码

看看SpringBoot提供的线程池部分源码Api

装饰器接口:

@FunctionalInterface
public interface TaskDecorator {
	Runnable decorate(Runnable runnable);
}

线程池Executor:

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
		implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
    
    // 装饰器增加线程任务
    @Nullable
	private TaskDecorator taskDecorator;
    
    @Nullable
	private ThreadPoolExecutor threadPoolExecutor;

	// Runnable decorator to user-level FutureTask, if different
	private final Map<Runnable, Object> decoratedTaskMap =
			new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK);
    
    // 注入
    public void setTaskDecorator(TaskDecorator taskDecorator) {
		this.taskDecorator = taskDecorator;
	}
    
    // 线程初初始化
    @Override
	protected ExecutorService initializeExecutor(
			ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

		BlockingQueue<Runnable> queue = createQueue(this.queueCapacity);

		ThreadPoolExecutor executor;
		if (this.taskDecorator != null) {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler) {
				@Override
				public void execute(Runnable command) {
                    // 包装工作线程,返回增加后的线程
					Runnable decorated = taskDecorator.decorate(command);
					if (decorated != command) {
						decoratedTaskMap.put(decorated, command);
					}
					super.execute(decorated);
				}
			};
		}
		else {
			executor = new ThreadPoolExecutor(
					this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS,
					queue, threadFactory, rejectedExecutionHandler);

		}

		if (this.allowCoreThreadTimeOut) {
			executor.allowCoreThreadTimeOut(true);
		}

		this.threadPoolExecutor = executor;
		return executor;
	}
    
    @Override
	protected void cancelRemainingTask(Runnable task) {
		super.cancelRemainingTask(task);
		// Cancel associated user-level Future handle as well
		Object original = this.decoratedTaskMap.get(task);
		if (original instanceof Future) {
			((Future<?>) original).cancel(true);
		}
	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wzq_55552

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值