在 spring bean 中处理线程安全的方法及其优缺点
Ways to handle thread-safety in spring beans and their pros & cons
我们在我的网络应用程序 (Jetty) 中使用邮件服务。服务对象在 运行 时使用 spring beans 注入。
现在,该服务不是线程安全的。我们该如何处理?在阅读这里的一些帖子时,我发现了以下选项。请让我们知道以下优点和缺点。
- 在邮件服务中同步方法(在 web-app 上下文中会好吗?)
- 每次都创建新对象 - 这也会很昂贵,因为初始化和释放需要时间
- 对象池
- ThreadLocal - 每个线程一个服务对象
Spring 配置:
<bean id="mailService" class="com.....MailService">
<property name="mailSender" ref="mailSender" />
<property name="registrationEmailMessage" ref="registrationEmail" />
...
</bean>
Java豆子
@Autowired
MailService mailService;
同步邮件服务中的方法。
通过阻塞进行同步会降低应用程序的速度,具体取决于同步块的执行时间。如果它是一个 singleton
bean,对 MailService
的调用将按顺序执行。即使它是一个 prototype
bean,它仍然可以同时从不同的线程调用,并且会导致顺序执行。
每次都创建新对象。如果它不消耗大量时间和资源,那么它是一个很好的解决方案。在方法中创建新对象作为局部变量,它们不会在线程之间共享。
线程本地。强大的概念,但有自己的问题:
- 变量可以随处访问,这违反了封装。可能导致诸如从 DAO 对象中的 ThreadLocals 中获取 HttpServletRequest 之类的实践。
- 容易引入内存泄漏。如果其中一个应用程序 类 在 ThreadLocal 变量中存储了一个值并且在手头的任务完成后没有将其删除,则该对象的副本将保留在线程中(来自应用程序服务器线程池)。由于池化线程的生命周期超过了应用程序的生命周期,它将阻止对象以及负责加载应用程序的类加载器被垃圾收集。我们已经制造了一个泄漏,它有机会以一个很好的旧 java.lang.OutOfMemoryError: PermGen space 形式出现。
考虑到它可能造成的危害,我们应该尽可能避免使用 ThreadLocal。
对象池。我支持这个解决方案。在这种情况下,线程安全可以通过编写一个新的线程安全优化的 silgleton 包装器 bean 来实现,在这种情况下,它将包含 MailService
个对象池。可以从任何线程安全地访问 Wrapper,并将执行重定向到池中。 Here 是一个例子。
我们在我的网络应用程序 (Jetty) 中使用邮件服务。服务对象在 运行 时使用 spring beans 注入。
现在,该服务不是线程安全的。我们该如何处理?在阅读这里的一些帖子时,我发现了以下选项。请让我们知道以下优点和缺点。
- 在邮件服务中同步方法(在 web-app 上下文中会好吗?)
- 每次都创建新对象 - 这也会很昂贵,因为初始化和释放需要时间
- 对象池
- ThreadLocal - 每个线程一个服务对象
Spring 配置:
<bean id="mailService" class="com.....MailService">
<property name="mailSender" ref="mailSender" />
<property name="registrationEmailMessage" ref="registrationEmail" />
...
</bean>
Java豆子
@Autowired
MailService mailService;
同步邮件服务中的方法。
通过阻塞进行同步会降低应用程序的速度,具体取决于同步块的执行时间。如果它是一个singleton
bean,对MailService
的调用将按顺序执行。即使它是一个prototype
bean,它仍然可以同时从不同的线程调用,并且会导致顺序执行。每次都创建新对象。如果它不消耗大量时间和资源,那么它是一个很好的解决方案。在方法中创建新对象作为局部变量,它们不会在线程之间共享。
线程本地。强大的概念,但有自己的问题:
- 变量可以随处访问,这违反了封装。可能导致诸如从 DAO 对象中的 ThreadLocals 中获取 HttpServletRequest 之类的实践。
- 容易引入内存泄漏。如果其中一个应用程序 类 在 ThreadLocal 变量中存储了一个值并且在手头的任务完成后没有将其删除,则该对象的副本将保留在线程中(来自应用程序服务器线程池)。由于池化线程的生命周期超过了应用程序的生命周期,它将阻止对象以及负责加载应用程序的类加载器被垃圾收集。我们已经制造了一个泄漏,它有机会以一个很好的旧 java.lang.OutOfMemoryError: PermGen space 形式出现。
考虑到它可能造成的危害,我们应该尽可能避免使用 ThreadLocal。
对象池。我支持这个解决方案。在这种情况下,线程安全可以通过编写一个新的线程安全优化的 silgleton 包装器 bean 来实现,在这种情况下,它将包含
MailService
个对象池。可以从任何线程安全地访问 Wrapper,并将执行重定向到池中。 Here 是一个例子。