Как избежать ConcurrentModificationExceptions при использовании EJB3.1 @Asynchronous

[Моя установка: приложение Java EE 6 с EJB3.1, CDI/Weld, JSF2, работающим на Glassfish 3.0.1]

Я читал несколько статей о новых @Asynchronous методах в EJB3.1, но ни в одной из них не упоминались опасности асинхронных методов и то, о чем вам действительно нужно заботиться.

В моем приложении у меня есть служба @Asynchronous E-Mail, отправляющая много писем. Я вызываю эту службу из компонента CDI/Weld. Во время моих тестов я часто сталкивался с 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? Какая разница?

Любые подсказки приветствуются!


person Wolkenarchitekt    schedule 02.08.2010    source источник


Ответы (2)


Ваше использование асинхронного метода должно было быть в порядке, хотя мне интересно, действительно ли вы хотите, чтобы он был @Stateful. Похоже, что состояние внутри bean-компонента @Stateful изменяется (или повторяется) в другом потоке, когда вызывается метод @Asynchronous. Это может произойти, если в bean-компоненте @Stateful есть, скажем, поле List, и ссылка на этот список передается за пределы bean-компонента @Stateful и используется. Если бы и вызывающий поток, и асинхронный поток использовали список, это было бы очень плохо, если бы вы не изменили его на какой-то параллельный список.

Если у вас есть состояние в bean-компоненте @Stateful, вам может быть лучше извлечь его в объект значения с конечными (неизменяемыми) полями и передать его методу @Asynchronous @Singleton — возможно, с помощью @Lock(READ), если асинхронный метод не обновляет никакого состояния в @Singleton.

person David Blevins    schedule 03.08.2010

Моя самая большая ошибка заключалась в использовании области сеанса для моего компонента CDI. Это позволяет одновременно использовать только один экземпляр асинхронного EJB, что, вероятно, приводит к ConcurrentModificationException (я думаю, что это происходит в момент, когда я переназначаю значение Future).

Таким образом, методы/классы @Asynchronous кажутся идеальными кандидатами для ConversationScope. Соответственно изменил мой компонент CDI, пока никаких исключений.

person Wolkenarchitekt    schedule 02.08.2010