【发布时间】:2012-11-26 02:06:44
【问题描述】:
我有一个 MDB(消息驱动 bean),它接收带有字符串的消息,表示一个单词。我也在数据库中有一个表。 MDB 应该在表中存储单词和每个单词被接收的次数(计数器)。
问题在于,为了获得更好的性能,MDB 在许多实例中启动,当不同实例接收到相同的新词时,它们都会创建计数为 1 的同一行。
为了解决这个问题,我应该使单词字段唯一,然后第二个实例将在提交时失败,重新传输消息,这将起作用,但可能会出现问题。这是一个好习惯吗?
另一种解决方案是在对计数器求和之后合并这些行。但是如果另一个实例会在更新过程中增加计数器呢?
如果两个实例试图增加计数器怎么办? @Version 应该够了吧?
我不确定这里的正确解决方案是什么。你会如何处理这种情况?
您能否推荐一些关于并发实践的书籍(不是关于使用synchronized,因为我需要支持Java EE 并且可能运行应用服务器集群)?
更新: 在阅读了有关 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;
}
所以问题是它会这样工作吗?我可以在不维护带有锁定行的数据库表的情况下做同样的事情吗?可能是一些 Java EE 容器锁定机制?
如果有帮助,我正在使用 JBoss 4.2。
对此我有一个新想法。我可以创建两个 MDB:
允许多个实例的第一个 MDB,它将处理所有消息,如果找不到该单词,则将该单词发送到第二个 MDB
仅允许一个实例的第二个 MDB,将串行处理消息并允许创建新单词
最好的部分:没有整个表/方法/进程锁定,只有计数器更新时的行锁定
这有多好?
谢谢。
【问题讨论】:
标签: java jakarta-ee concurrency locking