The name sounds like there are a lot of problems ahead. Here is my specific case:
This is a travel ticket sales system. Each route has a limited number of tickets, so buying the last ticket for this route should not be available to two people (standard scenario). However, there is a “return ticket” option. So, I use a unique route identifier (provided by the database) to do the following:
synchronized(bothRoutesUniqueString.intern()) { synchronized (routeId.intern()) { if (returnRouteId != null) { synchronized (returnRouteId.intern()) { return doPurchase(selectedRoute, selectedReturnRoute); } } return doPurchase(selectedRoute, selectedReturnRoute); } }
Two synchronized indoor units are designed to ensure that flows stop there only if a ticket for this particular route is purchased by two people at the same time, and not while purchasing tickets for two different routes. The second synchronization is, of course, because who Someone may try to acquire a retransmission route at the same time as an outbound route.
The outermost synchronized block must take into account the scenario when two people buy the same combination of tickets, vice versa. For example, one orders London-Manchester, and the other orders Manchester-London. If there is no external synchronized block, this situation can lead to a deadlock.
(The doPurchase() method returns a Ticket or throws an exception if there are no more tickets)
Now I understand perfectly that this is a very inconvenient solution, but if it works as expected, it gives:
- 10 lines to handle the whole complex script (and with the corresponding comments, it will not be so difficult to understand)
- there is no unnecessary blocking - everything blocks only if it should be blocked.
- agnosticism database
I also know that such scripts are handled either by pessimistic or optimistic database locks, and since I use Hibernate, it is also not difficult to implement.
I think horizontal scaling can be achieved using the above code using VM clustering. According to the Teracotta documentation , this allows you to turn a single-user node multi-threaded application into a multi-node and:
The terracotta track String.intern () calls and guarantees referential equality for these explicitly interned strings. Since all references to the interned String object indicate a canonical value, reference equality checks will work as expected even for distributed applications.
So now for the questions themselves:
- Have you noticed any flaws in the above code (other than its awkwardness)?
- Is there an applicable class from the
java.util.concurrent API to help in this scenario? - Why is database locking preferable?
Update: Since most of the answers concern OutOfMemoryError , I compiled a test for intern() and the memory was not eaten. Perhaps the row table is cleared, but it does not matter in my case, since I need the objects to be equal in race conditions, and the clearing of the last lines should not occur at the point:
System.out.println(Runtime.getRuntime().freeMemory()); for (int i = 0; i < 10000000; i ++) { String.valueOf(i).intern(); } System.out.println(Runtime.getRuntime().freeMemory());
PS Wednesday JRE 1.6