Threads 003: Server-Side Strategies

Hi, Ammendment: This article may inappropriately suggest that a single threaded server-side object using LockManager is significantly safer than a corresonding multi- threaded object. While the single-threaded, server-side object will contain less pitfalls, it is not a cure-all. And, I apologize if I have suggested that. For any server- side objects, whether single-threaded or multi-threaded, that access a shared resource, such as the LockManager, must be appropriately coded as if they were multi-threaded for that particular, critical part of the code.

*********************************** Threads003: Server-Side Strategies *********************************** Please see this linkhttp://www.coderanch.com/t/185138/java-developer-SCJD/certification/NX-Exam-Locking-Issues where the two basic approaches 1. the Multi-Threaded One Group, and 2. the Single-Threaded N Group are defined. Overview -------- This article gives a broad comparison of the different RMI-based server-side strategies, and gives my current opinion as to which is the best, both with respect to the using of resources and with respect to avoiding multi-threaded coding where it can be avoided. Remote Objects -------------- Depending on your design, the major remote object which the client uses to communicate with the database is either called something like DataImpl or MyServerImpl. For simplicity in this article, we will call this object DataImpl, but either form of object might be used if your assignment does not expressively forbid one or othe other. Server-Side Strategies ---------------------- The first item in the following list I thought of this morning, and this motivates the writing of this article: These server-side strategies are now listed:

One question which I have asked, and which has not been answered yet (though, of course it may have been answered in another thread, or in a way which did not register with me yet), is this: is RMI a toy? Or is it a serious production-based, networked solution? That is, how many clients can a normal, server support before the RMI system becomes too slow?

I. Multi-Threaded One-Instance DataImpl Shell ---------------------------------------------- This is a server-side strategy I thought of this morning, and at this time is quite probably how I will submit my exam. What I like about it is that if I were designing a production-based, real-world RMI solution, I would chose this solution; and, its multi-threaded complexity is low. This section outlines a production-based solution; however, keep in mind that I will most probably not implement a production-based solution since it is not required for the exam; thus, some components of this solution I will not implement, but the general concept I will most probably implement (or at least investigate, for I never know what I will actually submit): Diagram:

Concept: One DataImpl instance exists on the server. It delegates each business request to a single-threaded new DataWorker; each DataWorker, being single threaded, is easier to design, code and test, but at the cost that each new DataWorker temporarily uses up memory. When the DataWorker has carried out the business method request, it can be garbage collected. The strains on memory resources is short-lived, because DataWorker's life is short-lived. A production-based solution might determine in advance the maximum allowable number of instantiated DataWorker objects that will be allowed to exist on the server at any given time (since you don't want the server to run out of memory). This would be set in the server's preferences panel. A production-based solution, if it already had the maximum number of DataWorker objects instantiated and working, and another, new request came in, would find a way to delay carrying out this request until one of the current DataWorker objects was completed with its job. As it delays the requst, i.e., makes the incoming thread wait(), it might also determine just how many incoming threads in total can be made to wait (without exhausting memory or hogging the server's memory), and then after some limit has been reached, inform the user that the server is too busy. A production-based solution might find a way to re-use DataWorker objects, so you don't have to continually ask for a new one after an old one has carried out a business method. Be wary of making DataImpl overly complicated, however, since it is multi-threaded.

In this design, the Factory supplies the client with a unique, new, server-side DataImpl which is single-threaded. This is somewhat similar to the first design outlined in this article with the following difference: each instance of DataImpl (one per client) continues to exist as long as the client is connected to the server; for a production-based solution, I would not choose this design as it could, given a sufficiently large number of users, exhaust the server's memory. And, the number of RMI connections is unbounded. Furthermore, another drawback is that each server-side DataImpl object, has a life-time equal to that of how long each client stays connected, even though the actual work the client does while connected with respect to needing the server may be only 1/1000 or 1/10000 or 1/100000 of that time. For instance, when you use a web site, you spend 1/10 of a second accessing the page from the server, and you may spend one hour reading the web page.

"DataImpl_#1" means server-side instance number 1 of DataImpl. "DataImpl_#2" means the second server-side instance of DataImpl, and so forth. This is similar to the design given just above, but now each instance of DataImpl is potentially mult-threaded and serves M clients (it would be determined what the maximum number of clients one instance can serve efficiently would be). This is what happens for the default behavior of a servlet within a servlet container. Given one servlet class, then based upon the demand for that servlet, the servlet container decides how many servlet instances will be created, and how many client-request threads will be multi-threaded through any given instance. This was the design I was thinking of implementing until this morning I thought of the first design outlined in this article. Of course, here multi-threading issues abound, and it would take non-trivial skill to implement DataImpl correctly in this multi-threaded environment. A production version would dynamically instantiate DataImpl instances based upon server demand, multi-thread them up to a certain maximum based upon server demand, and destroy DataImpl instances when server demand drops. The fact that RMI does not do this coding for you, that is, the fact that RMI does not provide a servlet-like-container for you, may be what separates RMI as a toy while servlets are considered production-ready paradigms.