Java EE concurrency & locking

concurrencyjakarta-eejavalocking

I have a MDB (Message driven bean) that receives messages with String which represent a word. Also I have a table in the database. The MDB should store in the table, the words and the number of times each word was received (counter).

The problem is that to get better performance the MDB started in many instances and when the same new word is received by different instances they both create the same row with count of 1.

To solve this I should make the word field unique and then the second instance will fail on commit, retransmitting the message, which will work, but may be problematic. Is it a good practice ?

Another solution is to merge these lines afterwards summing the counter. But what if another instance will increase the counter in the middle of the update.

What if two instances try to increase the counter ? @Version should be enough?

I'm not sure what is the proper solution here. How would you handle such cases ?

Also can you suggest some books about concurrency practices (not about the use of synchronized as I need to support Java EE and may run a cluster of application servers)?


Update:
After reading more about EJB and JPA I suppose I want something like an locking entity. For example I can create a new table with only id and key columns and data like this:

ID | KEY
1  | WORDS_CREATE_LOCK

So that when I need to handle a new word I will do something like this (not exact code, not sure it will even compile):

// 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 ?

If it helps I'm using JBoss 4.2.


I have a new idea for this. I can create two MDBs:

1st MDB with many instances allowed, that will handle all the messages and if the word is not found will send the word to the second MDB

2nd MDB with only one instance allowed, will handle the messages serially and will allow creation of new word

The best part: no entire table/method/process locking, only row locking on counter update

How good is that ?

Thanks.

Best Answer

If you are looking for performance, no locking, etc. I would suggest to have another table: (word, timestamp). Your MDBs will just insert the word and the timestamp. Another process will count and update the table with the totals.