Spring和线程:异步操作

之前我们开始了在Spring中使用任务执行器TaskExecutor,这样对于在Spring应用中如何使用线程,我们就更熟悉了。

然而有时候使用任务执行器可能有些啰嗦,特别是我们只需要执行一个简单的动作时。

这时候Spring的异步方法就派上用场了。

相比把任务执行器TaskExecutor和可运行任务Runnable混到一起,你可以交出执行器的控制权,换做使用简单的异步功能。

为了在另外一个线程中执行你的功能,所有你需要做的事情是使用@Async注解你的功能。

Spring异步方法的使用有两种模式。

“发后即忘”模式 (fire and forget mode):一个没有返回值的方法。

@Async
@Transactional
public void printEmployees() {

    List<Employee> employees = entityManager.
            createQuery("SELECT e FROM Employee e").getResultList();
    employees.stream().forEach(e->System.out.println(e.getEmail()));
}

“结果提取”模式(results retrieval mode):返回一个Future对象的方法。

@Async
@Transactional
public CompletableFuture<List<Employee>> fetchEmployess() {
    List<Employee> employees = entityManager.
            createQuery("SELECT e FROM Employee e").getResultList();
    return CompletableFuture.completedFuture(employees);
}

需要额外注意一下这个事实,如果通过this来调用@Async注解的方法,注解@Async并不生效(译注:也就是说此时该方法会按照同步方式在当前线程执行)。这一点上,@Async@Transactional表现类似。因此你需要将你的异步方法声明为public(译注:声明为protected或者private是为了让子类或者自身实例使用,所以这里声明为public才跟从外部被调用的目标一致。)。从AOP代理文档中你可以看到更多的信息。

Spring应用中仅仅使用@Async注解想达到异步效果还不够,我们还需要配合使用另外一个Spring注解@EnableAsync启用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
@EnableAsync // 注意这个注解,在某个配置文件上使用该注解启用Spring异步执行能力
public class ThreadConfig {

    @Bean
    public TaskExecutor threadPoolTaskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(4);
        executor.setThreadNamePrefix("sgfgd");
        executor.initialize();

        return executor;
    }

}

下一个问题是,我们应该怎样声明异步功能将要使用的资源和线程池。从文档中我们可以得到答案:

缺省情况下,Spring会搜索一个关联的线程池定义:或者是上下文中定义的唯一一个TaskExecutor bean,或者是一个名字为”taskExecutor”的bean。如果两个都没有,就使用一个SimpleAsyncTaskExecutor来处理异步方法调用。

然而有些情况下,我们不希望使用同一个线程池运行所有的任务。我们可能想要分开配置多个线程池来完成我们的异步任务。

想做到这一点,我们向注解@Async传递我们想对每个功能任务使用的执行器的名字。

比如,我们配置了一个名字为‘specificTaskExecutor’ 的执行器 :

@Configuration
@EnableAsync
public class ThreadConfig {

    // 定义一个名字为specificTaskExecutor的任务执行器线程池bean
    @Bean(name = "specificTaskExecutor")
    public TaskExecutor specificTaskExecutor() { 
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.initialize();
        return executor;
    }

}

然后可以这么使用它 , 通过@Async中指定的执行器的限定名,异步功能会在指定的执行器中被执行 :

@Async("specificTaskExecutor")
public void runFromAnotherThreadPool() {
    // 该方法的逻辑会在名字为`specificTaskExecutor`的任务执行器的某个线程上执行
    System.out.println("你的异步任务");
}

下一篇文章我们会讲解线程上的事务。
你可以从github上找到本文中的代码。

英文原文

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/79838200