Java多线程异步任务

2 篇文章 0 订阅
1 篇文章 0 订阅

Java多线程——spring-boot 线程池 @Async 的使用、自定义Executor的配置方法

原文:https://blog.csdn.net/CJ_66/article/details/82503665

1、在启动类中添加@EnableAsync注解

@Slf4j
@EnableAsync
@SpringBootApplication
public class TestApplication implements CommandLineRunner{
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @Override
	public void run(String... args) throws Exception {
		log.info("TestApplication springboot start success!");
	}
}

2、自定义ThreadPoolTaskExecutor

创建一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来,线程任务总数、已完成数、活跃线程数,队列大小等情况一目了然。

/**
 * @Description : 自定义扩展Executor
 * @Author : SherlockerSun
 * @Date : 2022/9/1 17:24
 */
@Slf4j
public class VisibleThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {


    private void showThreadPoolInfo(String prefix){
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if(null==threadPoolExecutor){
            return;
        }
        
        log.debug("{}, {},taskCount [{}], completedTaskCount [{}],PoolSize [{}],CorePoolSize[{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getPoolSize(),
                threadPoolExecutor.getCorePoolSize(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }

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

    @Override
    public void execute(Runnable task, long startTimeout) {

        showThreadPoolInfo(task.getClass().getName()+"execute at:" + CommonUtils.formatDate(startTimeout));
        super.execute(task, startTimeout);
    }

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

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

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

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

打印日志工具方法

public class CommonUtils {

	/**
	 * @Description: TODO 格式化json数据,用于打印日志等
	 * @author: SherlockerSun
	 * @date 2022年8月26日下午6:53:30
	 */
	public static String writeFormat(Object obj) {
		
		return JSON.toJSONString(obj, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
		
	}
}

3、新建Executor配置类

/**
 * @Description : 
 * @Author : SherlockerSun
 * @Date : 2022/9/1 17:21
 */
@Configuration
@EnableAsync
public class ExecutorConfig {

    // 核心线程数
    private int corePoolSize = 50;
    // 最大线程数
    private int maxPoolSize = 500;
    // 阻塞队列容量
    private int queueCapacity = 400;

    @Bean("mySimpleAsync")
    public Executor mySimpleAsync() {
        //使用自定义ThreadPoolTaskExecutor
        ThreadPoolTaskExecutor executor = new VisibleThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("thread-simple-");
        // 这一步是关键,异步Task装饰器
        executor.setTaskDecorator(new MyContextDecorator());
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Bean("myAsync")
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new VisibleThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("thread-work-");
        // 这一步是关键,异步Task装饰器
        executor.setTaskDecorator(new MyContextDecorator());
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

这里定义了两个不同的Executor,第二个重新设置了pool已经达到max size时候的处理方法;同时指定了线程名字的前缀。

异步Task装饰器MyContextDecorator

public class MyContextDecorator implements TaskDecorator {

	@Override
	public Runnable decorate(Runnable runnable) {
		// TODO Auto-generated method stub
		// 获取主线程中的请求信息(我们的用户信息也放在里面)
	       RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
	        return () -> {
	            try {
	              	// 将主线程的请求信息,设置到子线程中
	              	RequestContextHolder.setRequestAttributes(attributes);
	             	// 执行子线程,这一步不要忘了
	                runnable.run();
	            } finally {
	            	// 线程结束,清空这些信息,否则可能造成内存泄漏
	                RequestContextHolder.resetRequestAttributes();
	            }
	        };
	}

}

4、创建一个AsyncTask类

在里面添加三个用@Async注解的task

实际项目中这里处理业务,通过future.get()方法获取线程处理的返回结果

@Slf4j
@Component
public class AsyncTask {

        @Async("mySimpleAsync")
        public Future<String> doTask1() throws InterruptedException{
            log.info("mySimpleAsync_Task1 started.");
            long start = System.currentTimeMillis();
            Thread.sleep(5000);
            long end = System.currentTimeMillis();

            log.info("mySimpleAsync_Task1 finished, time elapsed: {} ms.", end-start);

            return new AsyncResult<>("mySimpleAsync_Task1 accomplished!");
        }

        @Async("myAsync")
        public Future<String> doTask2() throws InterruptedException{
            log.info("myAsync_Task2 started.");
            long start = System.currentTimeMillis();
            Thread.sleep(3000);
            long end = System.currentTimeMillis();

            log.info("myAsync_Task2 finished, time elapsed: {} ms.", end-start);

            return new AsyncResult<>("myAsync_Task2 accomplished!");

    }

}

5、测试类

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TaskTests extends BasicUtClass {
    @Autowired
    private AsyncTask asyncTask;

    @Test
    public void AsyncTaskTest() throws InterruptedException, ExecutionException {
        Future<String> task1 = asyncTask.doTask1();
        Future<String> task2 = asyncTask.doTask2();

        while(true) {
            if(task1.isDone() && task2.isDone()) {
                log.info("Task1 result: {}", task1.get());
                log.info("Task2 result: {}", task2.get());
                break;
            }
            Thread.sleep(1000);
        }

        log.info("All tasks finished.");
    }
}

结果

2022-09-02 14:12:30.074  INFO 15812 --- [  thread-work-1] com.juc.config.AsyncTask : myAsync_Task2 started.
2022-09-02 14:12:30.074  INFO 15812 --- [thread-simple-1] com.juc.config.AsyncTask : mySimpleAsync_Task1 started.
2022-09-02 14:12:33.086  INFO 15812 --- [  thread-work-1] com.juc.config.AsyncTask : myAsync_Task2 finished, time elapsed: 3012 ms.
2022-09-02 14:12:35.081  INFO 15812 --- [thread-simple-1] com.juc.config.AsyncTask : mySimpleAsync_Task1 finished, time elapsed: 5007 ms.
2022-09-02 14:12:35.127  INFO 15812 --- [           main] com.juc.TaskTests        : Task1 result: mySimpleAsync_Task1 accomplished!
2022-09-02 14:12:35.127  INFO 15812 --- [           main] com.juc.TaskTests        : Task2 result: myAsync_Task2 accomplished!
2022-09-02 14:12:35.127  INFO 15812 --- [           main] com.juc.TaskTests        : All tasks finished.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中,可以使用多线程异步处理任务集中统一处理结果的方式有很多,其中比较常用的方式是使用线程池和Future对象。 1. 线程池:Java提供了Executor框架,可以通过创建线程池来管理多个线程。你可以使用ThreadPoolExecutor类来创建一个线程池,并将任务提交给线程池进行执行。通过使用线程池,你可以控制并发线程的数量,避免创建过多的线程导致资源浪费。当所有任务执行完成后,你可以通过线程池的回调机制来处理任务的结果。 2. Future对象:Future是Java提供的一个接口,它表示一个异步计算的结果。你可以通过ExecutorService.submit()方法提交一个任务,并返回一个Future对象。通过Future对象,你可以检查任务是否完成、取消任务的执行、获取任务的执行结果等。当所有任务都提交完成后,你可以遍历Future对象集合,获取每个任务的执行结果,并进行统一处理。 下面是一个示例代码,演示了如何使用线程池和Future对象来实现多线程异步处理任务集中统一处理结果: ```java import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class AsyncTaskProcessor { public static void main(String[] args) { // 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 创建任务列表 List<Callable<Integer>> tasks = new ArrayList<>(); tasks.add(new Task(1)); tasks.add(new Task(2)); tasks.add(new Task(3)); try { // 提交任务并获取Future对象列表 List<Future<Integer>> futures = executor.invokeAll(tasks); // 处理任务结果 for (Future<Integer> future : futures) { if (future.isDone()) { int result = future.get(); System.out.println("Task result: " + result); // 统一处理任务结果 // ... } } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } // 关闭线程池 executor.shutdown(); } static class Task implements Callable<Integer> { private int taskId; public Task(int taskId) { this.taskId = taskId; } @Override public Integer call() throws Exception { // 执行任务逻辑 Thread.sleep(1000); return taskId * 10; } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SherlockerSun

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

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

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

打赏作者

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

抵扣说明:

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

余额充值