使用EJB3.1 @Asynchronous时如何避免ConcurrentModificationExceptions

时间:2010-08-02 16:15:51

标签: asynchronous java-ee-6 cdi ejb-3.1 jboss-weld

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

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

在我的应用程序中,我正在使用@Asynchronous电子邮件服务,发送大量邮件。我从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);
    (...)
  }
}

我只是想问一下:我在这里做了一些完全错误的事情(范围,注射,使用未来)?使用@Asynchronous方法时,我需要注意什么,以避免ConcurrentModificationExceptions?

我将电子邮件注入EJB。将整个EmailEJB异步并用@Inject @Asynchronous注入它会更好吗?会有什么区别?

欢迎任何提示!

2 个答案:

答案 0 :(得分:2)

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

如果在@Stateful bean中确实有状态,最好将它提取到一个具有final(不可变)字段的值对象并将其传递给@Asynchronous @Singleton方法 - 可能使用@Lock(READ) )如果异步方法不更新@Singleton中的任何状态。

答案 1 :(得分:0)

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

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