0

[我的设置:Java EE 6 应用程序,EJB3.1、CDI/Weld、JSF2 在 Glassfish 3.0.1 上运行]

我阅读了一些关于 EJB3.1 中新的@Asynchronous 方法的文章,但没有一篇文章提到异步方法的危险以及您真正需要关心的内容。

在我的应用程序中,我有一个@Asynchronous E-Mail 服务,发送大量邮件。我从 CDI/Weld Bean 调用此服务。在我的测试过程中,我经常遇到 ConcurrentModificationExceptions,但直到现在我才真正理解它有时在哪里以及为什么会崩溃。

只是为了展示我的 Beans 大致是什么样子,重要的部分:

@Stateful @LocalBean
public class EmailEJB {
  //... Injections

  @Asynchronous
  public Future<Integer> sendEmails(User user, Message message) {
    // ... send mails
    return new AsyncResult<Integer>(1);
  }
}

在我的 CDI-Bean 中,我像这样使用这个 EJB(向 JSF2 公开进度):

@Named @SessionScoped 
public class MessageManager {
  @EJB 
  public EmailEJB emailEJB;

  public FutureEJB<Integer> progress;

  public Integer getProgress() {
    if (progress == null) return 0;
    else {
      return progress.get();
    }
  }

  public String sendMessage() {
    (...)
    progress = emailEJB.sendEmails(user, message);
    (...)
  }
}

我只是想问一下:我在这里做错了什么吗(范围、注入、使用 Future)?使用 @Asynchronous 方法时我需要注意什么以避免 ConcurrentModificationExceptions?

我将电子邮件作为 EJB 注入。把整个EmailEJB做成异步,用@Inject @Asynchronous注入会不会更好?会有什么区别?

欢迎任何提示!

4

2 回答 2

2

您对异步方法的使用应该没问题,但我想知道您是否真的希望它是@Stateful。当调用@Asynchronous 方法时,听起来@Stateful bean 中的状态正在另一个线程中被修改(或迭代)。如果@Stateful bean 有一个List 字段并且对该列表的引用被传递到@Stateful bean 之外并被使用,则可能会发生这种情况。如果调用者线程和异步线程都使用该列表,那将是一件非常糟糕的事情,除非您将其更改为某种并发列表。

如果您确实在@Stateful bean 中有状态,您最好将其提取到具有最终(不可变)字段的值对象中并将其传递给@Asynchronous @Singleton 方法——如果async 方法不会更新 @Singleton 中的任何状态。

于 2010-08-03T21:32:25.440 回答
0

我最大的失败是为我的 CDI bean 使用会话范围。这一次只允许异步 EJB 的一个实例 - 可能会导致 ConcurrentModificationException (我认为它在我重新分配 Future 值的地方)。

所以@Asynchronous 方法/类似乎是 ConversationScope 的理想候选者。相应地更改了我的 CDI bean,到目前为止没有例外。

于 2010-08-02T22:43:35.920 回答