Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java EE (J2EE) и Spring > Синхронизация потоков в spring mvc


Автор: toxx 17.10.2014, 21:30
Добрый день.

Хочу задать вопрос относительно синхронизации потоков в spring mvc и как такое решается, 
потому что не уверен в оптимальности своего решения.

Используемые технологии : Spring mvc, Hibernate.

Происходит регистрация номера(Одновременно операцию выполняют несколько пользователей), который генерируется на основе того, что сейчас есть в БД.
Ниже постараюсь описать схематично алгоритм и привести немного кода.

Сценарий такой:
1. В контроллере через сервис регистрируется новый номер
2. Берется максимальный номер последней зарегистрированной сущности
3. Этот номер используется для создания новой(схематично новыйНомер = номерПредыдущий + 1)
4. Номер должен быть сгенерирован уникальный

Код

@Service
public class ServiceImpl implements Service {

    @Override
    @Transactional
    public void register(...) {
         createNumber(...);
    }
    
    @Override
    public void createNumber(...) {
            .....
            Integer maxNumber = findMaxNumber(); // Ищется максимальный номер(без транзакции)
            entity.setNumber(maxNumber + 1);
            dao.update(entity); // update делается через hql
    }
}


Проблема возникает при одновременном выполнении операции регистрации.
Т.е. два пользователя одновременно решили сгенерировать номер, в результате чего метод
findMaxNumber возвращает один и тот же номер последней зарегистрированной сущности.
(Происходит это потому что сущность довольно тяжелая и не успевает сохраниться до того как другой поток вызывает findMaxNumber)
В результате сохраняются одинаковые номера в БД.

Вариант решения проблемы - сделать сервис синхронизации этой операции.

Код

@Service
public class SyncService {

    @Autowired
    private Service service;

    private Lock lock = new ReentrantLock();

    public void register(...) {
            try {
                lock.lock();
                service.register(...);
            } finally {
                lock.unlock();
            }
    }
}


Вариант работает.
Вопрос в том, какие есть еще способы синхронизации подобных ситуаций?
На сколько подходит данное решение для моей проблемы?

Спасибо.

Автор: d_k 20.10.2014, 07:49
А чем сиквенсы на БД не устраивают? Или автоинкремент поля.

Автор: toxx 20.10.2014, 20:07
d_k, 
Что вы имеете ввиду под сиквенсами и автоинкрементом? Если вы про это:
Код

entity.setNumber(maxNumber + 1);


То этот код только для примера, на самом деле поле строка и этот maxNumber одно из чисел,
которые участвуют в создании номера.

Автор: xoslv 9.2.2015, 17:43
Может стоит посмотреть в сторону Singleton? Т.е. объявляете класс как синглетон, а в методе 
Код

createNumber(){}

делаете синхронизацию.
Код

... createNumber(){
         synchronized(ServiceImpl.class){
            Integer maxNumber = findMaxNumber(); // Ищется максимальный номер(без транзакции)
            entity.setNumber(maxNumber + 1);
            dao.update(entity); // update делается через hql
       }
}

Как один из возможных вариантов реализации.
Только я бы наверное ещё добавил каких два метода и локальную переменную, для установки volatile флага.
Но следует учесть, что на высоко нагруженных системах такой метод может вызвать некоторое торможение.
В любом случае, при такой реализации нам нет необходимости думать с какой ДБ мы работаем и как там устроены локи.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)