Как избежать двойного создания состояния гонки с помощью JPA/Hibernate

Я в следующей ситуации.

Есть несколько потоков, которые могут читать/обновлять MonthlySales постоянные объекты. Проблема в том, что эти объекты могут не существовать и должны создаваться динамически по запросу.
Таким образом, два разных потока в конечном итоге создают сущность, соответствующую одному и тому же ключу (т.е. имеющую одинаковый идентификатор), поэтому один из них не удалось зафиксировать. Я не хочу, чтобы это произошло. Я хотел бы, чтобы один из двух потоков выиграл гонку, а другой проиграл, ожидая, пока объект не будет создан другим.

Другими словами, я хочу выполнять сериализованные транзакции. Должен ли я поставить явную блокировку Java (например, блок synchronized)?

Мой текущий код следующий:

MonthlySales ms = entityManager.find(MonthlySales.class, somekey);
if (ms == null) { // two threads might enter this block altogether
    ms = new MonthlySales();
    // prepare ms object populating its fields with default values
    entityManager.persist(ms);
    entityManager.flush();
}
entityManager.lock(ms, LockModeType.PESSIMISTIC_WRITE); // lock this object
                                                        // since we are going to update it
// do something with ms object
entityManager.getTransaction().commit();
entityManager.close();

Вы можете помочь мне?


person gd1    schedule 13.06.2012    source источник
comment
Вероятно, вам нужно явно управлять параллелизмом, аналогичный можно указать на stackoverflow.com/q/2992463/366964   -  person Nayan Wadekar    schedule 13.06.2012


Ответы (1)


Один из способов избежать состояния гонки — позволить выиграть потоку/процессу/и т. д. с наименьшим идентификатором.

Я считаю, что вы можете получить доступ к идентификатору потока, используя

long threadID = Thread.currentThread().getId();

из потока в Java. Это лучшее решение для условий гонки, чем блокирование других потоков, потому что это противоречит цели использования более одного потока.

См. Алгоритм пекарни критического раздела:

http://www.basicsofcomputer.com/critical_section_problem_in_operating_system.htm

В случае JPA, согласно здесь и здесь, наилучший практический подход, похоже, пытается изменить вещи одновременно время и ловить исключения, если что-то не получается.

person Alex W    schedule 13.06.2012
comment
Это решение ручной работы, мне нужна передовая практика, ориентированная на JPA. Более того, блокировка потоков не нарушает цели наличия более одного потока: это называется синхронизацией. Наконец, решение с наименьшим id-win было объявлено устаревшим, поскольку оно приводит к тому, что некоторые потоки зависают. Теперь у нас есть сложные алгоритмы для управления синхронизацией, учитывающие концепцию справедливости. - person gd1; 13.06.2012
comment
Я помню что-то из класса ОС, специально посвященное этому. Я считаю, что решение состояло в том, чтобы каждый процесс или поток в данном случае брал билет или номер, как в ресторане быстрого питания, чтобы попасть в критическую секцию. Тогда вы бы позволили потокам с наименьшим номером идти первыми. После того, как он покинет критическую секцию, если он захочет войти снова, он должен будет взять новый номер. Это должно сохранить справедливость и предотвратить голодную смерть. Я добавил ссылку на свой ответ. - person Alex W; 13.06.2012
comment
Хорошо, послушай, я ценю твои усилия, но мы не пишем ОС на ассемблере. :) Я ищу Java-решение проблемы Java, и это должно быть что-то максимально абстрактное и высокоуровневое. Меня не интересуют низкоуровневые, одноразовые решения. Однако решение моей проблемы заключается в сериализации транзакций посредством явной синхронизации. См.: stackoverflow.com/questions /2992463/ - person gd1; 13.06.2012
comment
Я просто пытался дать низкоуровневые решения на случай, если вам придется реализовать это самостоятельно, однако я добавил эту ссылку в свой ответ, когда опубликовал свой последний комментарий. - person Alex W; 13.06.2012