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;
/**
 * Created by gkatzioura on 4/26/17.
 */
@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;
/**
 * Created by gkatzioura on 4/26/17.
 */
@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;
/**
 * Created by gkatzioura on 10/18/17.
 */
@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;
/**
 * Created by gkatzioura on 4/26/17.
 */
@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上找到本文提到的源代码。

英文原文

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/79839264
今日推荐