Java EE并发和锁定

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

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

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

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

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

我不确定这里的解决方案是什么。 你会如何处理这类案件?

您也可以建议一些关于并发实践的书籍(不是因为我需要支持Java EE而使用synchronized ,而是可能运行应用程序服务器集群)?


更新:在阅读了有关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 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; } 

所以问题是它会以这种方式工作吗? 如果不维护带锁定行的DB表,我可以这样做吗? 可能是一些Java EE容器锁定机制?

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


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

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

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

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

这有多好?

谢谢。

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

这听起来需要通过选择正确的事务隔离级别来解决数据库中的问题 – 可重复读取应该足够了。

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

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

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