Як не слід ініціалізувати spring-beans у багатопотоковому середовищі
Інтродукція
Великий проект на Spring. IOC, Autowiring, діла-дрова…
Є группа споріднених бінів, що хитромудро кастомно ініціалізуються. Щоб звести їх ініціалізацію докупи, є ще один бін, який має їх усіх приавтовайреними своїми полями. Після прив'язки своїх полів, цей бін запускає їх ініціалізацію. Все класно, все ініціалізується. З метою покращити перформанс, ініціалізацію споріднених бінів переведено в паралельний режим…
Проблема
В паралельному режимі виникають дедлоки. Тривалий пошук виявив, що проблема стосується не взаємовідносин бінів у «родині», а що лочить їх оцей самий ініцалізуючий бін… Гм, якого?..
Ліричний відступ
«Так, треба щоб цей бін ініціалізував усіх отих, але після того, як вони будуть до нього прив'язані. тобто після встановлення усіх властивостей, чи, як кажуть англійці, afterPropertiesSet(). Туди ми ініціалізіцію і прикрутимо… Так, працює, чудово. А тепер ще асинхронний варіант...»
Ср@^а, або Звідки ноги ростуть
AbstractBeanFactory наслідується від DefaultSingletonBeanRegistry, в котрому у багатьох методах використовується блокування по полю singletonObjects. Зокрема, таке блокування відбувається під час виклику метода getBean(). Штука в тому, що дія метода afterPropertiesSet() потрапляє «всередину» цього блокування, і… і коли у нас один потік, то все нормально, бо блокування належить йому. А коли потоків стає багато, починаються дедлоки (головний потік чекає завершення «відбрунькованих»).
Вирішення
Не пхати кастомну ініціалізацію у afterPropertiesSet(). Зробити окремий метод і викликати його з іншого місця, після того, як контейнер видасть сформований ініціалізуючий бін.
- +5
- 23 серпня 2010, 17:30
- manuna
Коментарі (6)
RSS згорнути / розгорнутиНо на сколько я понял, вы пытаетесь несколькоми потоками одновременно инициализировать спринг-контекст. Ну так оно и без бубна понятно, что дело не пойдет. Сначала инициализация, потом, потом проверка, потом вызов.
afon
zlosny
Мабуть трохи невдало використано термін "ініціалізація" («щоб цей бін ініціалізував усіх отих») — мається на увазі не спрінгова, а вже бізнес-логічна. Тобто не вибирання та взаємозв'язування об'єктів через контейнер, а подальше наповнення їх певним чином отриманими та обробленими даними.
manuna
GrAndSE
цей новий бін буде містити afterPropertiesSet яка власне і буде робити пост-ініціалізацію та зачистку.
p.s. якось не попадались багатопотокові ініціалізації, але я був майже впевнений що з afterPropertiesSet не все чисто ;)
zenyk
Так.
Ні.
Дійсно, з цього «ще одного» біна ми можемо викликати метод того, що паралельно ініціалізує. Але тільки не з afterPropertiesSet — привіт, дедлоки. У моєму випадку виклик відбувався, грубо кажучи, з main.
manuna
Тільки зареєстровані й авторизовані користувачі можуть залишати коментарі.