[我的设置: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注入它会更好吗?会有什么区别?
欢迎任何提示!
答案 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,到目前为止没有例外。