java - new Thread().start内部类的使用场景?
阿神
阿神 2017-04-18 09:19:21
0
4
1564

下面的这个方法是发邮件的抽象出来的一个公用方法:

String[] to 表示收件人列表;
subject  邮件主题;
templateName 邮件末班,用velocity写的,
Map params 参数,用来填充velocity中的某些字段取值的
public void sendHtmlWithTemplate(String[] to, String subject, String templateName, Map<String, Object> params) {
    final MimeMessage mimeMessage = mailSender.createMimeMessage();
    try {
        final MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage);
        messageHelper.setFrom(simpleMailMessage.getFrom());
        if (ENV_ONLINE.equals(environment)) {
            messageHelper.setTo(to);
            messageHelper.setSubject(subject);
        } else {
            messageHelper.setTo(adminEmail);
            messageHelper.setSubject(subject + Arrays.asList(to));
        }
        messageHelper.setSentDate(new Date());
        final String content = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, templateName, "UTF-8",
                                                                           params);
        final String[] logTo = to;
        messageHelper.setText(content, true);
        new Thread() {

            @Override
            public void run() {
                mailSender.send(mimeMessage);
                logger.error("Mailsentto: " + Arrays.asList(logTo) + "\nContent: " + content);
            }
        }.start();
    } catch (Exception e) {
        logger.error("emailServiceError error:" + e.getMessage(), e);
    }
}

在上面的发邮件的代码中,使用了内部类如下:

new Thread() {
    @Override
    public void run() {
        mailSender.send(mimeMessage);
        logger.error("Mailsentto: " + Arrays.asList(logTo) + "\nContent: " + content);
    }
}.start();

我觉得在这个地方做这个控制是很恰当的吧,为什么team leader让我删掉这个new Thread()的部分。
原话:“把new Thread全部删掉,这些邮件发送不了的bug都没有暴露出来; 邮件发送模块的代码采用异步发送方式,失去异常事务回滚的能力,new Thread要全部删除;” 不懂是什么意思啊?

问题:
1.为什么要去掉new Thread()?
2.在什么样的并发业务场景下,需要使用new Thread()这样的方式?有没有更好的解决方法?
3.在并发场景下,使用new Thread().start()的方式有什么弊端?每次new Thread新建对象性能会很差么?邮件服务也是当触发某个业务规则的时候,可能需要大量发送一下邮件,用线程池好不好呢?

阿神
阿神

闭关修行中......

全部回覆(4)
伊谢尔伦

@有明 已經說了下匿名內部類比較蛋疼的地方。我再結合你的業務來說一下:

  1. 根據現有的程式碼邏輯,我們郵件服務對外暴露的應該是sendHtmlWithTemplate这个方法,而不是 mailSender.send,所以在我们对外暴露的sendHtmlWithTemplate這個方法裡面就不需要再重啟一個線程異步去發送郵件,因為對於調用方,其實你需要告訴它這次發送郵件是否成功,如果失敗了/異常了,失敗的原因是什麼。如果按照你現有的方案的話,可能給外面感覺就是,不​​管郵件發送成功或者失敗,外面都是沒有感知的,意味著不能將這封郵件持久化,並且過一段時間再重試。所以非同步發送郵件應該交給你的呼叫方,而不是在你的方法裡面

  2. 假設我們的發送郵件可能是一個很耗時的動作,那麼按照每來一封郵件就啟一個線程去做的話,意味這到時候系統裡面會產生很多線程。那麼就意味會有很多線程切換,線程的切換,線程的切換就會有阻塞和喚醒,這些都涉及到用戶態和內核態的轉換或者是線程的上下文的切換,都是很耗CPU的,極端情況如果系統線程不段堆積的話,會導致整個服務DOWN機,服務處於不可用的狀態。 new Thread()確實是個很昂貴的操作

  3. 如果正確的做非同步發送郵件,確實需要放到線程池裡面做

  4. 更加的好的方法,一種就是透過MQ來發送郵件訊息(考慮訊息常見蛋疼的問題1.訊息是否會遺失2.訊息重發了怎麼辦),單獨交給一台發送郵件的伺服器去做。還有一種就是先存入資料庫表,如果時效性要求不是很高的話,到時候啟一個定時器慢慢發。

小葫芦

匿名物件很方便,但匿名物件也是魔鬼,使用不好就能讓你的應用程式崩塌。就拿你這裡做例子,如果你沒有控制好發送郵件的參數,比如發送時超時了就繼續等待,那這個線程可就變成了孤魂野鬼了,找也找不到,關也關不掉,這是程式設計師該做的事情嗎?
你問為何你的頭讓你把這裡去掉,我只能說是他還不夠相信的你能力,所以不敢讓你操縱這個魔鬼。
你問什麼場景下適合,我只能說沒有適合的場景,只有有能力的人。

黄舟

如果要發大量的郵件,可以做個佇列,線程太多的話,恐怕不太好。

Peter_Zhu

不好意思,現在才看到問題。

具體參考@iMouseWu 和 @有明 的答案。

如果你有寫過js,你可以理解這裡的new Thread()相当于ajax請求,是屬於異步行為,在這個線程任務還沒執行完之前,方法已經返回給調用方了(而且不會拋出發送郵件產生的異常) ,這也就是你leader說的「這些郵件發送不了的bug都沒有暴露出來」

考慮並發量大的話可以採用redis+kafka(或其他訊息元件),用訊息的形式去通知郵件服務發送郵件。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板