Spring 框架中的线程池
原文合集地址如下,有需要的朋友可以关注
Spring 框架中的线程池
使用 Java 的 ExecutorService 接口实现
ExecutorService
是 Java 提供的用于管理线程池的高级工具。
下面是在 Spring 框架中使用线程池的一般步骤:
导入所需的依赖
首先,确保你的项目中包含了使用线程池所需的依赖。通常情况下,你可以使用 Spring Boot 来创建项目,它会自动包含线程池相关的依赖。
创建线程池
在 Spring 中,你可以通过配置文件或使用 Java 代码来创建线程池。如果你使用配置文件,可以在application.properties
或application.yml
文件中配置线程池的属性。如果你使用 Java 代码,可以通过创建ThreadPoolTaskExecutor
或ThreadPoolExecutor
对象来实现。
在需要使用线程池的地方注入它
在你的代码中,如果你需要使用线程池来执行异步任务或并发处理,你可以通过在需要使用的地方注入线程池来实现。
提交任务给线程池
一旦你注入了线程池,你就可以使用它来提交任务。
或者,如果你需要获取任务的结果,你可以使用submit()
方法。
销毁线程池
在 Spring 应用程序关闭时,确保销毁线程池以释放资源。
以上是在 Spring 框架中使用线程池的基本步骤。你可以根据自己的需求和具体的场景来配置线程池的属性和使用方式。
使用 @Async 注解使用异步线程池执行任务
在 Spring 框架中,使用@Async
注解可以将方法标记为异步执行的方法,使其在调用时会被自动提交到线程池中执行,而不会阻塞主线程。
以下是使用@Async
注解的步骤:
导入所需的依赖
确保你的项目中包含了使用@Async
注解所需的依赖。通常情况下,你可以使用 Spring Boot 来创建项目,它会自动包含相关的依赖。
启用异步支持
在 Spring 配置中,你需要启用异步支持。如果你使用 Java 配置方式,可以在配置类上加上@EnableAsync
注解。如果你使用 XML 配置方式,可以在 XML 配置文件中添加<task:annotation-driven executor="taskExecutor" />
。
在需要异步执行的方法上添加@Async
注解
将@Async
注解添加到你希望异步执行的方法上。
配置线程池并指定任务执行器名称(可选)
Spring 框架默认使用SimpleAsyncTaskExecutor
作为任务执行器,但你也可以自定义线程池并指定任务执行器的名称。在配置类中创建一个TaskExecutor
的 bean,并通过@Async
注解的value
属性或Executor
参数指定任务执行器的名称。
关于 SimpleAsyncTaskExecutor
SimpleAsyncTaskExecutor
是 Spring 框架提供的默认的任务执行器,它是基于 Java 的Executor
接口实现的简单的线程池。
要配置SimpleAsyncTaskExecutor
,你可以在 Spring 的配置类中创建一个TaskExecutor
的 bean,并将其返回。以下是一个示例:
在上述示例中,我们创建了一个TaskExecutor
的 bean,并将其返回。SimpleAsyncTaskExecutor
的实例被创建,并通过setConcurrencyLimit()
方法设置了并发限制,这是可选的,默认为无限制。setThreadNamePrefix()
方法设置了线程名的前缀,以便于识别任务执行的线程。
你可以根据需要进行其他配置,SimpleAsyncTaskExecutor
还提供了其他方法,例如setThreadGroupName()
、setDaemon()
等,可以根据具体需求进行设置。
在使用SimpleAsyncTaskExecutor
时,它会根据需要创建新的线程来执行任务,不会重用线程。因此,它适用于短期、简单的异步任务,但对于长期运行的任务或需要复杂线程池配置的场景,可能需要使用其他实现,如ThreadPoolTaskExecutor
。
请注意,SimpleAsyncTaskExecutor
不适合高负载的应用程序,因为它没有线程池的管理机制,并且没有对队列容量、拒绝策略等进行配置。如果你的应用程序需要更高级的线程池管理功能,建议使用ThreadPoolTaskExecutor
或其他支持更多配置选项的任务执行器。
关于 ThreadPoolTaskExecutor
ThreadPoolTaskExecutor
是 Spring 框架提供的一个基于 Java 的ThreadPoolExecutor
的封装,它提供了更多的线程池管理功能和配置选项。
要配置ThreadPoolTaskExecutor
,你可以在 Spring 的配置类中创建一个TaskExecutor
的 bean,并进行相应的配置。以下是一个示例:
在上述示例中,我们创建了一个TaskExecutor
的 bean,并将其返回。ThreadPoolTaskExecutor
的实例被创建,并通过一系列的set
方法进行配置,包括:
setCorePoolSize(int corePoolSize)
: 设置核心线程数,即线程池中保持的线程数量。setMaxPoolSize(int maxPoolSize)
: 设置最大线程数,即线程池允许的最大线程数量。setQueueCapacity(int queueCapacity)
: 设置队列容量,即线程池任务队列的最大容量。setThreadNamePrefix(String threadNamePrefix)
: 设置线程名的前缀,以便于识别任务执行的线程。initialize()
: 初始化线程池。
除了上述方法,ThreadPoolTaskExecutor
还提供了其他配置选项,如setKeepAliveSeconds()
、setAllowCoreThreadTimeOut()
、setRejectedExecutionHandler()
等,可以根据具体需求进行设置。
配置完成后,你可以将ThreadPoolTaskExecutor
用作任务执行器,通过在@Async
注解中指定任务执行器的名称或通过注入使用它。
请注意,ThreadPoolTaskExecutor
提供了线程池的管理功能,可以根据任务的情况自动调整线程池的大小,对于长期运行的异步任务或需要更高级的线程池管理的场景,它是更常用的选择。
调用异步方法
在其他组件或类中,直接调用带有@Async
注解的异步方法即可。
@Async
注解可以在后面加入一个名字,用于指定具体的任务执行器,如上面的示例中的@Async("taskExecutor")
。这样做的好处是,你可以针对不同的异步任务使用不同的线程池或任务执行器。
注意事项:
异步方法必须定义在 Spring 管理的 Bean 中,因为 Spring 会通过代理来拦截方法调用并将其提交给线程池。
使用
@Async
注解时,如果异步方法内部发生了异常,调用者不会收到异常,因为异步方法在独立的线程中执行。若需要捕获异常,可以使用Future
来获取异步任务的执行结果并处理异常。确保在 Spring 的配置中启用了异步支持,否则
@Async
注解将不起作用。
这些是使用@Async
注解实现异步方法的基本步骤,可以根据具体需求进行配置和使用。
@Async 的实现原理
@Async 注解的实现原理使用了 AOP、动态代理、Java 反射、线程池、Runnable 和 Callable 接口、Future 接口、Java 并发类库等技术和类库,通过这些技术和类库的组合,实现了异步方法的调用和执行。
异步方法的代理生成
当 Spring 扫描到带有 @Async 注解的方法时,它会生成一个代理对象来封装该方法。
以下是 Spring 框架中生成代理对象的代码示例:
在上述代码中,AsyncAnnotationBeanPostProcessor
实现了BeanPostProcessor
接口,用于在 Bean 初始化之前对带有@Async
注解的方法进行处理。当 Spring 扫描到带有@Async
注解的方法时,postProcessBeforeInitialization()
方法会被调用。
在postProcessBeforeInitialization()
方法中,首先通过AopUtils.getTargetClass(bean)
获取目标类的类型,然后遍历目标类的所有方法。对于每个方法,使用AnnotationUtils.findAnnotation(method, Async.class)
查找是否有@Async
注解。
如果找到了带有@Async
注解的方法,就会调用createAsyncProxy()
方法创建代理对象。在createAsyncProxy()
方法中,通过ProxyFactory
创建一个代理工厂,并设置目标对象、添加asyncAdvisor
(异步处理的通知器),以及其他配置。最后调用proxyFactory.getProxy()
获取代理对象,并将其返回。
通过以上代码,Spring 框架在扫描到带有@Async
注解的方法时,会生成一个代理对象来封装该方法,实现异步调用的功能。这样,在调用带有@Async
注解的方法时,实际上是通过代理对象来调用的,从而实现了异步执行。
任务执行器的选择
根据 @Async 注解的配置,Spring 会根据指定的任务执行器名称选择相应的任务执行器。如果没有指定名称,它将使用默认的任务执行器。
在 Spring 中,根据@Async
注解的配置选择任务执行器的过程涉及以下几个关键的源码部分:
AsyncAnnotationBeanPostProcessor
类
该类是一个BeanPostProcessor
,用于处理带有@Async
注解的方法。在方法postProcessBeforeInitialization()
中,它会检查方法上的@Async
注解的配置,并根据配置选择任务执行器。
AsyncConfigurer
接口
该接口定义了配置任务执行器的方法。你可以通过实现该接口来自定义任务执行器的配置。其中,getAsyncExecutor()
方法用于返回一个Executor
对象,即任务执行器。
AnnotationAsyncExecutionInterceptor
类
该类是一个 AOP 的MethodInterceptor
,用于在调用带有@Async
注解的方法时进行拦截。在invoke()
方法中,它会检查方法上的@Async
注解的配置,并从AsyncAnnotationInterceptor
中获取任务执行器。
AsyncAnnotationInterceptor
类
该类是一个 AOP 的MethodInterceptor
,用于处理带有@Async
注解的方法。在invoke()
方法中,它会获取任务执行器,并使用任务执行器来执行异步任务。
相关的源码示例
在上述代码中,AsyncAnnotationBeanPostProcessor
类通过getExecutor()
方法获取任务执行器,该方法会优先从AsyncConfigurer
中获取执行
器,如果未配置,则从 Spring 容器中获取TaskExecutor
bean。然后,在创建代理对象时,将任务执行器传递给AnnotationAsyncExecutionInterceptor
。
AnnotationAsyncExecutionInterceptor
类的invoke()
方法中,使用获取到的任务执行器来执行异步任务,通过executor.submit(task)
将任务提交给执行器。
通过上述源码,当 Spring 扫描到带有@Async
注解的方法时,会根据配置获取任务执行器,并将其传递给代理对象,在执行异步方法时使用相应的任务执行器。这样就可以根据配置选择不同的任务执行器来处理异步方法的调用。
方法调用的封装
代理对象在调用异步方法时,会将该方法的执行请求封装成一个 Runnable 或 Callable 的任务,并提交给任务执行器。任务执行器会负责管理线程池,并在合适的时间执行异步任务。
在 Spring 中,代理对象在调用异步方法时,会将该方法的执行请求封装成一个Runnable
或Callable
的任务,并提交给任务执行器。这个过程涉及以下关键的源码部分:
AnnotationAsyncExecutionInterceptor
类
该类是一个 AOP 的MethodInterceptor
,用于处理带有@Async
注解的方法。在invoke()
方法中,它会将异步方法的执行请求封装成Runnable
或Callable
的任务对象。
AsyncExecutionInterceptor.AsyncExecutionRunnable
类和AsyncExecutionInterceptor.AsyncExecutionCallable
类
这两个类分别实现了Runnable
和Callable
接口,用于封装异步方法的执行逻辑。
TaskExecutor
接口和其实现类
Spring 提供了多个实现了TaskExecutor
接口的任务执行器,例如ThreadPoolTaskExecutor
、SimpleAsyncTaskExecutor
等。任务执行器负责管理线程池,并执行提交的任务。
相关源码示例
在上述代码中,AnnotationAsyncExecutionInterceptor
类的invoke()
方法中,首先根据@Async
注解的配置信息,创建一个AsyncExecutionRunnable
对象,将任务执行逻辑封装在其中。
接着,通过调用任务执行器的execute()
方法,将AsyncExecutionRunnable
对象提交给任务执行器执行。任务执行器在内部会从线程池中获取一个空闲的线程,并执行run()
方法中的异步方法调用。
当异步方法执行完成或出现异常时,任务执行器会进行相应的处理。
通过以上源码,可以看出代理对象在调用异步方法时,会将异步方法的执行请求封装成一个Runnable
或Callable
的任务,并提交给任务执行器来执行。这样就实现了异步方法的调用和执行。
异步任务的执行
任务执行器会从线程池中获取一个空闲的线程来执行异步任务。异步任务的执行可能会被放入线程池的任务队列中,直到有线程可用为止。
异步方法的返回值处理
如果异步方法有返回值,代理对象会返回一个 Future 对象,用于获取异步方法执行的结果。你可以通过 Future 对象来获取异步方法的返回值或处理异常。
更多异步线程池的库或框架
除了使用@Async
注解和自定义线程池外,在 Spring 框架中,还可以结合其他异步线程池的库或框架来实现异步任务的执行。以下是一些常见的异步线程池的库或框架:
Guava
Guava 是 Google 提供的 Java 工具库,其中包含了一个ListeningExecutorService
接口,可以将ExecutorService
转换为支持异步操作的执行器。
CompletableFuture
CompletableFuture
是 Java 8 引入的一个类,提供了一种方便的方式来执行异步任务。可以结合CompletableFuture
和Executor
来实现异步任务的执行。
用法
CompletableFuture
是 Java 8 中引入的一个类,用于支持异步编程和处理异步任务的结果。它提供了一种方便的方式来处理异步操作,并支持将多个异步任务组合在一起。下面是CompletableFuture
的一些常用用法以及如何与 Spring Boot 一起使用:
创建一个异步任务:
调用异步任务的结果:
组合多个异步任务:
异步任务的异常处理:
与 springboot 集成
与 Spring Boot 一起使用CompletableFuture
时,你可以将异步任务作为 Spring Bean 的方法,并使用@Async
注解进行标记,以便在 Spring 容器中进行管理和调用。以下是一个示例:
在 Spring Boot 应用程序的配置类中,启用异步支持:
在 Spring Bean 中定义一个异步方法,使用
CompletableFuture
进行异步操作:
在其他组件中调用异步方法,并处理异步结果:
通过将异步任务定义为 Spring Bean 中的方法,并使用@Async
注解进行标记,Spring Boot 将自动为这些方法创建异步代理,使它们能够在单独的线程中执行。然后,你可以在其他组件中调用这些异步方法,并使用CompletableFuture
来处理异步任务的结果。
在异步操作时,相比于Executors.newSingleThreadExecutor().submit
,使用CompletableFuture.supplyAsync
的优势
相比于Executors.newSingleThreadExecutor().submit
,使用CompletableFuture.supplyAsync
有以下几个好处:
异步任务的创建和执行更加简洁:使用
CompletableFuture.supplyAsync
可以直接将异步任务的逻辑封装在 Lambda 表达式中,并将其作为参数传递给方法。这样可以更加简洁地创建和执行异步任务,而不需要显式地创建Executor
和Callable
对象。支持更多的组合和链式操作:
CompletableFuture
提供了丰富的方法来处理异步任务的结果。你可以使用thenApply
、thenAccept
、thenCombine
等方法来对异步任务的结果进行转换、处理和组合。这使得在异步任务之间进行链式操作和数据流转变变得更加便捷。支持异常处理和超时控制:
CompletableFuture
提供了方法来处理异步任务的异常情况,如exceptionally
、handle
等。它还支持设置异步任务的超时时间,通过get
方法的重载版本来控制等待异步结果的超时时间。这样可以更好地管理和处理异步任务中可能出现的异常和超时情况。更灵活的执行器选择:
CompletableFuture
允许你传入自定义的执行器(Executor
)来执行异步任务。这样你可以根据需求选择不同类型的执行器,如固定大小线程池、缓存线程池等,以满足特定的并发需求。支持函数式编程:
CompletableFuture
采用了函数式编程的风格,通过方法链式调用和 Lambda 表达式来组织异步任务的逻辑。这种风格更加符合现代 Java 开发的趋势,并提供了更高的可读性和可维护性。
综上所述,使用CompletableFuture.supplyAsync
相比于Executors.newSingleThreadExecutor().submit
具有更多的优势,能够提供更灵活、更简洁、更可组合的异步任务编程方式。
Apache Commons
Apache Commons 库提供了ExecutorService
的实现,例如BasicThreadFactory
和DefaultExecutorServiceFactory
,可以用于创建和配置线程池。
BasicThreadFactory
它是一个简单的线程工厂,用于创建线程并配置其属性。你可以使用它来自定义线程的命名、优先级、守护状态等。
示例代码:
DefaultExecutorServiceFactory
它是一个用于创建ExecutorService
实例的工厂类。你可以使用它来创建具有自定义属性的ExecutorService
,如核心线程数、最大线程数、线程存活时间等。
示例代码:
这些ExecutorService
的实现类可以根据你的需求进行配置和使用,以满足不同的并发处理需求。可以根据自己的项目需要选择合适的实现类,并根据需要设置线程池的属性和配置。
Netflix Hystrix
Hystrix 是 Netflix 开发的容错库,提供了异步执行和线程池隔离等功能。它可以与 Spring 集成,用于处理异步任务的执行和容错机制。
这些库或框架提供了更多的功能和选项,可以根据具体需求选择合适的异步线程池库或框架来实现异步任务的执行。在 Spring 中,可以通过配置或自定义TaskExecutor
来集成这些库或框架,并使用它们来执行异步任务。
评论