This week's book giveaway is in the OCPJP forum. We're giving away four copies of OCA/OCP Java SE 7 Programmer I & II Study Guide and have Kathy Sierra & Bert Bates on-line! See this thread for details.
Hi, The following is a review of locking schemes in general for comparison purposes. This is a strategic review. Please feel free to correct any mis-notions I may have. The following assumptions are made: * the database is stored as one, random access file, * the database is accessed through the class Data. * low level file read, update, write, and create new record operations are carried out within only one instantiated class called MicroData where each method is synchronized. There is no overhead processing associated with any of these methods in that each method directly reads, updates, writes, or creates a new record in the file. * although not explicitly drawn, there exists one instantiation of the singleton class called NLock, which is involved with logical record locking (has lock() and unlock() methods and is multi-threaded). Exactly how it is integrated and used by Data or MyServer or by some other object is not shown. The use of the term "singleton" does not mean that you should actually declare any of your objects as singletons; but, since this example assumes only one database file, "singleton" is used here for clarity. There are numerous sub-cases:
In the above chart, "1" means at most one instance. "N" means at most N instances, where each instance is tied to one client uniquely; so, if there are 4 clients, there will be 4 unique server-side objects. For cases 1 and 2, Data is a remote object. For cases 3, 4, and 5, MyServer is a remote object, and Data is not. Let us now consider sub-cases having to do with the processing that occurs nearest to the DbRaf. The following sub-cases are extracted from the above figure:
Remember that every method in MicroData is synchronized. The question is, is this necessary for both cases? 1. Since the one instance of Data is multi-threaded, there is no doubt that MicroData's methods must be synchronized. 2. Since the N instances of Data are each single-threaded, this is just the same as case 1: multiple threads using one shared resource called MicroData. Thus, again, MicroData's methods must be synchronized. I don't see any benefit in considering the following design: 3. Data(N) ---each uses its own, unique MicroData(N) --> DbRaf I see no benefit from doing it this way, for you would still have to introduce one singleton object which the MicroData method's would synchronize on. [Aside: if you had more than one Database file type, then this might be something to consider.] Now let's look at the client-side. We will not assume that the client is only one thread. For, it is always possible, like a shopping cart, the a user might pre-select 10 reservations (for a large party visiting a certain town), and then with one click, reserve them all. The client implementation, it will be assumed, is allowed to send multiple, concurrent threads to one remote object. Important Warning I Just Saw:
If we assume one client can multi-thread a server-side remote object, then we are in big trouble if we use the client ID as a unique ID! This is something I will do for the exercise of learning, that is, I want the client ID or some variation of this to end up in a WeakHashMap; however, for real, production code, I may not let the client touch Data, for then I get two benefits: 1. The server-side remote code can be multiply instantiated and multi-threaded (so, it's ready to become a Servlet without too much effort), 2. The client, instead of manipulating Data, manipulates MyServer, so I then don't have to worry about "deadbeat" clients. End of warning.
With the above assumption, this means that every object on the server-side must assume that it might be multi-threaded. Thus, here are the concurrency issues on the server-side: 1. Any remote object must be coded to handle multiple, concurrent threads. 2. Any remote object must be coded to handle shared resources within a concurrent threading environment. 3. Any remote object must be aware that it may not be the only instantiation of itself. Given this, our diagram, in general changes to this:
which reduces, in effect, as far as what we need be aware of during the design to this:
and whether there is one ServerSideObject or N of them, the way we code is not changed (given the assumption added so far). Thus, the major, strategic categories, first listed as five items at the top of this article, reduces to the following (when we assume that one client can send multiple, concurrent threads to any remote object):
which reduces to
And, if we don't worry about the MyServer, since it is a middle-man, our core processing choices have now reduced to one:
We now introduce NLock as a multi-threaded singleton whose job it is to coordinate access to individual records (reminder: we are not dealing with exactly which classes will use NLock, only that NLock has to exist in the design somewhere and that there will only be one instantiation of this multi-threaded object):
We need to remind ourselves, that within MySever(1..N) (not shown just above), and Data(1..N), the following would be illogical:
For the corrected versions, you declare static, final sync objects:
If you assume that your server-side objects can potentially have one or more instantiations, then any instance member must become a class member if it is to be shared between these potential instances:
If you assume that your remote objects can be multi-threaded and can be instantiated more than once, then you are making the same assumptions you would make for the default behavior of a servlet; this may, perhaps, help slightly in your score as your remote objects could easily be turned into servlets. But, this is pure speculation with respect to getting a better score. Thanks, Javini Javono [ February 08, 2004: Message edited by: Javini Javono ]