Пружинные транзакции могут не синхронизировать синхронизированный метод?

У моего коллеги и меня есть веб-приложение, которое использует Spring 3.0.0, и JPA (будьте в спящем режиме 3.5.0-Beta2) на Tomcat в MyEclipse. Одна из структур данных является деревом. Только для забавы, мы попробовали стресс-тестирование, "вставляют узел" операция с JMeter и нашел проблему параллелизма. Будьте в спящем режиме отчеты, находящие два объекта с тем же закрытым ключом, сразу после предупреждения как это:

 WARN  [org.hibernate.engine.loading.LoadContexts] fail-safe cleanup (collections) : ...

Довольно легко видеть, как такие проблемы могли бы произойти, если несколько потоков называют вставку () методом одновременно.

Мой сервлет вызовы, уровень служб возражает B.execute (), который затем называет нижний уровень, возражает C.insert (). (Реальный код является слишком большим для регистрации, таким образом, это несколько сокращено.)

Сервлет A:

  public void doPost(Request request, Response response) {
    ...
    b.execute(parameters);
    ...
  }

Сервис B:

  @Transactional //** Delete this line to fix the problem.
  public synchronized void execute(parameters) {
    log("b.execute() starting. This="+this);
    ...
    c.insert(params);
    ...
    log("b.execute() finishing. This="+this);
  }

Подсервис C:

  @Transactional
  public void insert(params) {
    ...
    // data structure manipulation operations that should not be 
    // simultaneous with any other manipulation operations called by B.
    ...
  }

Все мои вызовы изменения состояния проходят B, таким образом, я решил сделать B.execute () synchronized. Это уже было @Transactional, но это - на самом деле бизнес-логика, которая должна синхронизироваться, не только персистентность, так, чтобы казался разумным.

Мой C.insert () метод был также @Transactional. Но так как распространение транзакции по умолчанию в Spring, кажется, Требуется, я не думаю, что была любая новая транзакция, создаваемая для C.insert ().

Все компоненты A, B, и C являются пружинными бобами и следовательно одиночными элементами. Если действительно существует только один объект B, то я прихожу к заключению, что это не должно быть возможно больше чем для одной угрозы выполнить b.execute () за один раз. Когда загрузка легка, только единственный поток используется, и дело обстоит так. Но при загрузке, принимают участие дополнительные потоки, и я вижу, что несколько печати потоков "запускаются", прежде чем первый распечатает "окончание". Это, кажется, нарушение synchronized природа метода.

Я решил распечатать this в сообщениях журнала, чтобы подтвердить, было ли только единственный объект B. Все сообщения журнала действительно показывают тот же идентификатор объекта.

После большого расстраивающего расследования я обнаружил то удаление @Transactional для B.execute () решает проблему. С той строкой, которую уводят, у меня может быть много потоков, но я всегда вижу "запуск", сопровождаемый "окончанием" перед следующим "запуском" (и мои структуры данных остаются неповрежденными). Так или иначе, synchronized только, кажется, работает когда @Transactional не присутствует. Но я не понимаю почему. Кто-либо может помочь? Какие-либо подсказки относительно того, как изучить это далее?

В отслеживаниях стека я вижу, что существует прокси aop/cglib сгенерированный промежуточный A.doPost () и B.execute () - и также между B.execute () и C.insert (). Интересно, могла ли так или иначе конструкция прокси разрушить synchronized поведение.

5
задан John 2 February 2010 в 11:06
поделиться

1 ответ

Синхронизированное ключевое слово требует, как вы заявили, что объект вовлечен всегда одинаково. Я не наблюдал вышеупомянутое поведение, но ваш подозреваемый может быть просто правильным.

Вы пытались выйти из B от DOPOST-Method? Если это отличается каждый раз, то происходит некоторая весенняя магия с прокси AOP / CGLIB.

В любом случае, я бы не полагался на синхронизированное ключевое слово, но использую что-то вроде ReentrantLock от Java.util.concurrent.Locks, чтобы обеспечить вместо этого поведение синхронизации, так как ваш объект B всегда является независимо от возможных Несколько прокси CGLIB.

2
ответ дан 14 December 2019 в 08:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: