【问题标题】:Is JavaMailSenderImpl of spring thread-safe?Spring的JavaMailSenderImpl是线程安全的吗?
【发布时间】:2016-06-12 17:54:23
【问题描述】:

我正在使用 spring 中的 JavaMailSenderImpl 在我的 Web 应用程序中发送电子邮件。我只创建了一个实例(实际上它被另一个使用spring bean创建的对象使用,并且是一个单例)。

所以问题是,JavaMailSenderImpl 是线程安全的吗?在我的应用程序中,当多个线程同时使用 mailSender 时,会导致任何竞争条件吗?

【问题讨论】:

  • 是的,一旦构建它就是线程安全的。
  • @M.Deinum 我在查看源代码时也有同样的想法,但不确定。您能否详细说明并添加答案,以便我接受。如果您能解释多个线程如何同时发送邮件,那就太好了。

标签: spring multithreading mail-sender


【解决方案1】:

是的,JavaMailSenderImpl 是线程安全的一旦它被构造。

看看实际工作的doSend 方法。它仅包含方法局部变量(因此每个调用线程/堆栈都有自己的实例)。 (这同样适用于添加一些功能的send 方法)。

getSession 之类的方法是 synchronized,因此只有单个线程可以访问该方法。

使它成为线程安全的最重要的一点是,(几乎)没有可变共享状态,只有一个可变共享状态(Session)是synchronized

除此之外,它已经在生产系统中以单例方式使用了超过 12 年,并且从未遇到过任何并发问题。是的,我们在高度并发的应用程序中使用了它。 (这也是 Spring Batch 和 Spring Integration 等其他框架组件使用JavaMailSender API 的方式)。

【讨论】:

  • 但是看看field loading的顺序。用户名和密码是在getSession调用之前加载的,所以即使在构造之后存在一些同步点,这里也没有happens-before边缘。
  • 正确...但这是您一开始不应该做的事情...而且我声明在构造后使用它是安全的... Spring bootstraps 在一个线程中。 getUsernamegetPassword 也被调用一次,然后在本地而不是全局使用。这同样适用于框架的其他类(它们都包含一个注释:** 此类的一个实例在配置后是线程安全的。**。)。
  • @m-deinum,但是connectTransport 可以从不同的线程(每个doSend)调用多次。因此,如果该线程在 spring-bootstrap 线程之前启动,它可以从 getUsername 读取 null,因为没有发生之前的边缘。 (我同意你的观点,这是非常不切实际的情况,尤其是对于 x86,但我在这里看不到任何 JMM 正确性证明)
  • 那又怎样...传输没有缓存,每次调用时都会重新创建。执行此操作的线程可能会收到无法发送电子邮件的异常。每次调用 doSend 都会重新创建传输。
  • 接下来,除非您自己创建新实例,否则您无法在 spring 初始化之前获取 bean 的实例。因此,在您甚至可以访问实例之前,必须先引导 Spring。并且保证引用 bean 的对象得到一个初始化的 bean。所以除非你在那之后搞乱用户名和密码,否则在 spring 上下文中以正常方式使用它可以使其基本上是线程安全的。
【解决方案2】:

它是线程安全的,但值得补充的是,它只能支持有限数量的并发调用。当我的第 4 个线程调用 sendMail 函数时,我收到以下错误:

org.springframework.mail.MailSendException:消息失败:com.sun.mail.smtp.SMTPSendFailedException:432 4.3.2 STOREDRV.ClientSubmit;超出发件人线程限制 [主机名=***]。因此,您应该捕获运行时异常并将其记录下来,否则它只会被运行该线程的执行程序服务吞没。

【讨论】:

    【解决方案3】:

    不,不是。

    虽然it has 在其源代码中进行了一些同步,但用户、密码等字段没有同步访问权限。因此,如果您在一个线程中调用setUsername,则其他线程无法保证可见性。

    【讨论】:

    • 我只使用 spring 创建了一次对象,并设置了邮件服务器身份验证和 javaMailProperties 的所有必需字段。我不会再改变那些了。创建对象后,多个线程(码头)使用它并行发送邮件。这会是一个问题吗?我是专门询问邮件发送部分的。
    • Here,getter 在第一个同步点之前被调用,所以根据happens-before 它不是线程安全的,
    猜你喜欢
    • 1970-01-01
    • 2013-04-04
    • 2012-12-18
    • 2011-10-08
    • 2016-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    相关资源
    最近更新 更多