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
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
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
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
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.
[ February 08, 2004: Message edited by: Javini Javono ]