Spring和线程:TaskExecutor

当你想在Web应用中运行长时间的任务时,别忘了使用Spring的TaskExecutor,它能帮你管理相关组件。

Web应用中使用线程也不是什么罕见的事情,尤其是你需要开发长时间运行的任务时。

Spring中,我们必须特别注意并使用它已经提供的工具,而不是使用我们自己新建线程的方式。

我们希望我们的线程能够被Spring管理,这样就既能使用应用程序的其他组件,也不会带来任何坏影响。我们也希望优雅地关闭应用程序,而不是关闭的时候还有些工作还没完成。

Spring提供了TaskExecutor作为任务执行者的抽象。

Spring的TaskExecutor接口与java.util.concurrent.Executor接口相同。
Spring发行版中包含了许多预先构建的TaskExecutor实现,你可以从官方文档中找到更多关于它们的信息。

通过为Spring环境提供一个TaskExecutor实现,你将能够将TaskExecutorbean注入到其他bean中并可以访问托管线程:

package com.gkatzioura.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class AsynchronousService {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    // 注入一个TaskExecutor实现
    @Autowired
    private TaskExecutor taskExecutor;
    
    // 该方法是同步的,但是其调用taskExecutor.execute()中的run()会在另外一个
    // 线程中异步执行
    public void executeAsynchronously() {
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                // 这里是需要异步执行的长时间任务逻辑
            }
        });
    }
}

想像上面那样使用TaskExecutor,第一步其是将TaskExecutor配置添加到我们的Spring应用程序中:

package com.gkatzioura.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;

@Configuration
public class ThreadConfig {
	// 定义一个类型为TaskExecutor的bean,方法名字无所谓
    @Bean
    public TaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setThreadNamePrefix("default_task_executor_thread");
        executor.initialize();
        return executor;
    }
}

一旦我们配置了执行器(译注:也就是上面配置的TaskExecutor bean),任务处理就很简单了。我们将执行器TaskExecutor bean注入到另外的Spring组件,然后提交包含要执行任务的Runnable对象就可以了。

因为异步代码可能需要与应用程序的其他组件进行交互,所以最好的方法是创建原型作用域(prototype)的Runnable bean实例然后注入。例子如下:

package com.gkatzioura;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class MyThread implements Runnable {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyThread.class);
    @Override
    public void run() {
        LOGGER.info("Called from thread");
    }
}

然后,我们就可以将执行器(上面类型为TaskExecutor的executor)注入我们的服务并使用它来执行Runnable实例了。


package com.gkatzioura.service;
import com.gkatzioura.MyThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class AsynchronousService {
    @Autowired
    private TaskExecutor taskExecutor;
    @Autowired
    private ApplicationContext applicationContext;
    public void executeAsynchronously() {
	    // 获取我们自定义的作用域为prototype的可执行任务Runnable bean
        MyThread myThread = applicationContext.getBean(MyThread.class);
        // 使用 taskExecutor对其进行异步执行
        taskExecutor.execute(myThread);
    }
}

在下一篇文章中,我们将通过使用Spring的异步函数将我们的多线程代码库提升到一个新的层次。

你可以在GitHub上找到本文提到的源代码。

英文原文

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值