Як не слід ініціалізувати spring-beans у багатопотоковому середовищі

Інтродукція
Великий проект на Spring. IOC, Autowiring, діла-дрова…
Є группа споріднених бінів, що хитромудро кастомно ініціалізуються. Щоб звести їх ініціалізацію докупи, є ще один бін, який має їх усіх приавтовайреними своїми полями. Після прив'язки своїх полів, цей бін запускає їх ініціалізацію. Все класно, все ініціалізується. З метою покращити перформанс, ініціалізацію споріднених бінів переведено в паралельний режим…
Проблема
В паралельному режимі виникають дедлоки. Тривалий пошук виявив, що проблема стосується не взаємовідносин бінів у «родині», а що лочить їх оцей самий ініцалізуючий бін… Гм, якого?..

Ліричний відступ
«Так, треба щоб цей бін ініціалізував усіх отих, але після того, як вони будуть до нього прив'язані. тобто після встановлення усіх властивостей, чи, як кажуть англійці, afterPropertiesSet(). Туди ми ініціалізіцію і прикрутимо… Так, працює, чудово. А тепер ще асинхронний варіант...»

Ср@^а, або Звідки ноги ростуть
AbstractBeanFactory наслідується від DefaultSingletonBeanRegistry, в котрому у багатьох методах використовується блокування по полю singletonObjects. Зокрема, таке блокування відбувається під час виклику метода getBean(). Штука в тому, що дія метода afterPropertiesSet() потрапляє «всередину» цього блокування, і… і коли у нас один потік, то все нормально, бо блокування належить йому. А коли потоків стає багато, починаються дедлоки (головний потік чекає завершення «відбрунькованих»).

Вирішення
Не пхати кастомну ініціалізацію у afterPropertiesSet(). Зробити окремий метод і викликати його з іншого місця, після того, як контейнер видасть сформований ініціалізуючий бін.
  • +5
  • 23 серпня 2010, 17:30
  • manuna

Коментарі (6)

RSS згорнути / розгорнути
+
+1
Что-то вы столько всего наговорили, что я сомневаюсь, сможете ли сами понять свой пост, абстрагировавшись от проекта, в котором, как я понимаю, плотно и долго варитесь.

Но на сколько я понял, вы пытаетесь несколькоми потоками одновременно инициализировать спринг-контекст. Ну так оно и без бубна понятно, что дело не пойдет. Сначала инициализация, потом, потом проверка, потом вызов.
avatar

afon

  • 24 серпня 2010, 00:05
+
0
ну не будем судити надто строго. тим, хто серйозно використовував spring буде більш-менш зрозуміло.
avatar

zlosny

  • 24 серпня 2010, 15:11
+
-1
Намагався писати якнайабстрагованіше від проекта, без подробиць :)
Мабуть трохи невдало використано термін "ініціалізація" («щоб цей бін ініціалізував усіх отих») — мається на увазі не спрінгова, а вже бізнес-логічна. Тобто не вибирання та взаємозв'язування об'єктів через контейнер, а подальше наповнення їх певним чином отриманими та обробленими даними.
avatar

manuna

  • 25 серпня 2010, 09:44
+
0
При моїх досить незначних пізнаннях в Spring все цілком зрозуміло. Тут скоріше треба вміти в голові тримати як все виглядає вцілому та розуміти, що таке багатопоточне програмування, дедлок, наслідування і т.п. Вся Spring-термінологія або пояснена, або є очевидною, так що не переживайте — все цілком зрозуміло без додаткових пояснень :)
avatar

GrAndSE

  • 25 серпня 2010, 11:13
+
0
можна зробити ще один бін, який буде залежний на той який паралельно ініціалізує всі попередні біни.
цей новий бін буде містити afterPropertiesSet яка власне і буде робити пост-ініціалізацію та зачистку.

p.s. якось не попадались багатопотокові ініціалізації, але я був майже впевнений що з afterPropertiesSet не все чисто ;)
avatar

zenyk

  • 31 серпня 2010, 11:53
+
0
можна зробити ще один бін, який буде залежний на той який паралельно ініціалізує всі попередні біни.

Так.
цей новий бін буде містити afterPropertiesSet яка власне і буде робити пост-ініціалізацію та зачистку.

Ні.
Дійсно, з цього «ще одного» біна ми можемо викликати метод того, що паралельно ініціалізує. Але тільки не з afterPropertiesSet — привіт, дедлоки. У моєму випадку виклик відбувався, грубо кажучи, з main.
avatar

manuna

  • 31 серпня 2010, 12:39

Тільки зареєстровані й авторизовані користувачі можуть залишати коментарі.