Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > 2 потока вытягивают из БД одну и ту же задачу


Автор: barracuda477 1.5.2009, 21:41
Есть несколько потоков, они вытягивают из БД (через Hibernate) по одной задачке (через fetchAndLockTopPriorityTask) и обрабатывают каждую задачу .
Выполнение каждой задачи занимает от 1 до 10 секунд.

Код метода fetchAndLockTopPriorityTask() приведён ниже и делает следующее:
1) находит 1 топ-задачу со статусом "новая задача"
2) изменяет статус этой задачи в "задача в обработке" (чтобы задача не могла быть вытянута другим потоком)

Метод fetchAndLockTopPriorityTask() реализован в инстансе фабрики TaskBOF. 
Каждому потоку при конструировании передан один и тот же инстанс этой фабрики.

Проблема в том, что иногда (воспроизводимо в течении 50-200 секунд) 2 потока вытягивают из БД одну и ту же задачу.

Код

....

public class TaskBOF extends TaskGenBOF {

.....

  /**
   * Reruns next task for processing (picks up with highest priority)
   * @return Task or null if no task for processing available
   */
  public synchronized Task fetchAndLockTopPriorityTask() {
    Session session = getSessionFactory().openSession();
    Criteria criteria = session.createCriteria(Task.class);
    criteria.add(Restrictions.eq("status", TaskStatus.New.getStatus()));
    criteria.addOrder(Order.desc("priority"));
    criteria.setMaxResults(1);
    List<Task> tasks = criteria.list();
    session.close();
    if (tasks.size() > 0) {
      Task taskForProcessing = tasks.get(0);
      taskForProcessing.setStatus(TaskStatus.Processing.getStatus());
      update(taskForProcessing);
      return taskForProcessing;
    } else {
      return null;
    }
  }

.....

/**
* Updates object into Database
* @param obj
*/
  public void update(Object obj) {
    Session session = getSessionFactory().openSession();
    Transaction transaction = session.beginTransaction();
    session.update(obj);
    transaction.commit();
  } 


Лог вытягивания задач (задачи с одинаковым ИД могут отстоять друг от друга на несколько секунд)

20:52:35,109  INFO QueueProcessorThread:113 - Thread #4:Fetched task:249956;
20:52:37,062  INFO QueueProcessorThread:113 - Thread #2:Fetched task:250212;
20:52:37,546  INFO QueueProcessorThread:113 - Thread #3:Fetched task:250468;
20:52:41,015  INFO QueueProcessorThread:113 - Thread #6:Fetched task:250212;


Автор: NeoNYura 1.5.2009, 22:12
А как потоки создаешь?


можно контролировать на уровне БД, а можно на уровне клиента к бд.

1)Если на уровне БД, то в базе можно выставить запрет на одновременный доступ к таблице, пока не закрыл сессию
2)можно на уровне клиента, например доступ бд через синглтон.





Автор: barracuda477 1.5.2009, 23:04
Потоки делаю вот так:

Код

...

public class QueueProcessor  {
    
 ...

  private TaskBOF taskBof = new TaskBOF();
  private int threads = 6;

    public static void main (String[] parameters) throws Exception {
        QueueProcessor qp = new QueueProcessor();
        qp.startProcessing();
  }

  /**
   * Creating and launches processing threads
   * @throws Exception - any exception type
   */
  private void startProcessing() throws Exception {
    setAllProcessingTasksToNew();
    for (int i = 1; i <= threads; i++) {
      QueueProcessorThread queueProcessorThread = new QueueProcessorThread(i, taskBof, statistics);
      new Thread(queueProcessorThread).start();
    }
  }


Цитата

1)Если на уровне БД, то в базе можно выставить запрет на одновременный доступ к таблице, пока не закрыл сессию
2)можно на уровне клиента, например доступ бд через синглтон.


1) Нет, лочить всю таблицу не хочу, тем более что как видно по логу - задачи с одинаковым ИД могут отстоять друг от друга на несколько секунд.
2) Ну как бы у меня Хиберенейт и синглтон тут выткать не имеет смысла smile 
Кстати, как я указал - запрет на одновременный доступ к методу fetchAndLockTopPriorityTask() реализован через synchronized с блокировкой расшаренной между всеми потоками фабрики.

Думаю на какое-то кеширование в хибренейте, но пробовал по всякому (setCacheMode, setFlushMode) - не помогло

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