首页 文章

Java EE并发和锁定

提问于
浏览
4

我有一个MDB(消息驱动的bean),它接收带有String的消息,代表一个单词 . 我在数据库中也有一个表 . MDB应该在表格中存储单词和每个单词被接收的次数(计数器) .

问题是为了获得更好的性能,MDB在许多实例中启动,并且当不同实例接收到相同的新单词时,它们都创建计数为1的同一行 .

为了解决这个问题,我应该使单词字段唯一,然后第二个实例将在提交时失败,重新传输消息,这将起作用,但可能有问题 . 这是一个好习惯吗?

另一种解决方案是在对计数器求和之后合并这些线 . 但是如果另一个实例会在更新过程中增加计数器呢?

如果两个实例试图增加计数器怎么办? @Version 应该够了吗?

我不确定这里的解决方案是什么 . 你会如何处理这种情况?

你也可以推荐一些关于并发实践的书籍(不是关于 synchronized 的使用,因为我需要支持Java EE并且可能运行一组应用程序服务器)?


Update: 在阅读了有关EJB和JPA的更多信息后,我想我想要一个类似锁定实体的东西 . 例如,我可以创建一个只包含id和key列的新表,数据如下:

ID | KEY
1  | WORDS_CREATE_LOCK

因此,当我需要处理一个新单词时,我会做这样的事情(不是确切的代码,不确定它甚至会编译):

// MAIN FUNCTION
public void handleWord(String wordStr) {
  Word w = getWord(wordStr);

  if (w == null)
    w = getNewOrSychronizedWord(wordStr);

  em.lock(w);
  w.setCounter(w.getCounter() + 1);
  em.unlock(w);
}

// Returns Word instance or null if not found
private Word getWord(String wordStr) {
  Word w = null;

  Query query = em.createQuery("select w from words as w where w.string = :wordStr order by w.id asc");
  query.setParameter("wordStr", wordStr);
  List<Word> words = query.getResultList();

  if (words.getSize() > 0)
    w = words.get(0);

  return w;
}

// Handles locking to prevent duplicate word creation
private Word getNewOrSynchronizedWord(String wordStr) {
  Word w = null;
  Locks l = em.find(WORDS_CREATE_LOCK_ID, Locks.class);
  em.lock(l);

  Word w = getWord(wordStr);

  if (w == null) {
    w = new Word(wordStr);
    em.persist(w);
  }

  em.unlock(l);
  return w;
}

So the question is will it work that way? And can I do the same without maintaining a DB Table with locking rows? May be some Java EE container locking mechanism ?

如果它有助于我使用JBoss 4.2 .


我有一个新想法 . 我可以创建两个MDB:

允许多个实例的第一个MDB,它将处理所有消息,如果找不到该字,则将该字发送到第二个MDB

仅允许一个实例的第二个MDB将连续处理消息并允许创建新单词

最好的部分:没有整个表/方法/进程锁定,只有计数器更新时的行锁定

这有多好?

谢谢 .

3 回答

  • 1

    如果你正在寻找性能,没有锁定等,我建议有另一个表:( word,timestamp) . 您的MDB将只插入单词和时间戳 . 另一个进程将使用总计计算和更新表 .

  • 1

    这听起来需要通过选择正确的transaction isolation级别在数据库中解决 - 可重复读取应该足够了 .

    你需要的是一本关于数据库的书,专注于交易 .

  • 2

    您是指多个实例正在处理相同的消息,还是在不同的消息中使用相同的单词?如果它是相同的消息,那么您应该使用队列而不是主题 . 这当然不能解决多个消息中同一个词的问题 . 对于这种情况,您可以遵循@Michael Borgwardt和@Vitaly Polonetsky的建议 .

    数据库之外的另一个选项是让不同的MDB实例处理以一组字母开头的单词 . 这可以通过选择器轻松完成 . 然后只有一个MDB处理任何特定的单词,但处理仍然在多个实例之间分开以提高性能 . 我并不是说这是一个更好的选择,而只是支持非常简单的基于队列的处理 .

相关问题