First Java

    A. First(Second...) Edition

    This is the first assignment of comp6231 which is concentrated on distributed system design and this assignment is required

    to use RMI. And believe it or not, this is actually my first java program because in comp346 the java program is not written

    by me but it is given by professor. So, this is the first one.

    ¡¡

    {I should say this is actually the last version which I can only make it right long after my assignment is submitted. Indeed

    I am now struggling with assginment3. However, after correctly solve synchronization problem in assignment2, assignment1

    becomes quite easy. The most important thing I learned from assignment2(CORBA) is the exact picture of thread model at

    server side. Yesterday morning, I quickly browse the UnicastremoteObject in sun.com and it confirms what I expect. By

    inheritating from  this class which implements some interfaces for multi-threading, you are able to allow java server to

    create multiple instance of your implementation class.}

    B.The problem

Programming Problem:
3. Java RMI
In the programming assignments, you are going to develop a simple distributed banking system (DBS). Our bank comprises a set of branches. Each branch manages a disjoint subset of the bank's accounts. Associated with each account is a unique account number¡ªseven digit integer (the first three digits indicate the branch and the next four digits indicate the unique account within that branch), a balance¡ªfloating point number, as well as other information (like name, address and contact information of the account holder) that will be of less concern to us. Customers may invoke the following operations on accounts:
• Open(acnt): Opens a new account and returns the (randomly generated) account number acnt.
• Deposit(acnt, amt): Cause the balance of account number acnt to be increased by the specified amount amt. Returns the new account balance.
• Withdraw(acnt, amt): Cause the balance of account number acnt to be decreased by specified amount amt. Returns the new (possibly negative) account balance.
• Balance(acnt): Returns the balance of account number acnt.
This application basically has a BankServer implementing the above operations and a BankClient invoking these operations as necessary. For simplicity, assume that the account information of a branch (that is, all the accounts and their balances) is stored in a table accessed by the account number and stored in persistent storage.
In this assignment you are going to develop this application using Java RMI. Specifically, do the following.
• Write the Java RMI interface definition for the BankServer with the specified operations.
• Implement the BankServer.
• Design and implement a BankClient which invokes the server to test the correct operation of the DBS.
¡¡
C.The idea of program
¡¡

Design Choices:

1. Design of packages

package | files included | class or interface | Super
BankInterface | BankInterface.java, | BankInterface | Remote
BankException.java | BankException | RemoteException
BankServer | BankInterfaceImplement.java | BankInterfaceImplement | UnicastRemoteObject
DataBase |
BranchManager |
Server.java | Server |
BankClient | Client.java | Client |

2. Design of interface

a) Originally I want to mimic "DCOM" which always uses return value of SUCESS or FAILURE to indicate whether a remote procedure is successful or not. However, java doesn't allow me to return a parameter by passing by reference. So, I have to preserve return value for each function call in interface. And in order to uniformly handle both system error and logical error, I use remote exception to indicate any error or exception at remote server. (see details in design of exception)

b) As both client and server needs to reference interface during compilation, it is very natural for me to put some constants inside interface like RMI port number, number of branches etc.

3. Design of Database

a) In order to speed up searching and simplify synchronization, I store data of one branch in one file. i.e. there are ten branches in my design and there are ten database files for them.

b) Usually we store each record linearly in database file. However, unless we implement an index file there is no way for fast access to records in searching. Therefore, I used a RandomAccessFile object to speed up searching. This comes from the observation of a simple fact that there are 0 to 9999 account number in each branch. Therefore I store the balance of each account in file like a big array. Initially the balance for each account is initialized to -1 to indicate unopened account. When opening a new account, my file pointer uses "seek" method to move to offset of accountNumber*SizeOfAccount from beginning of file. Then write a new value of 0 to the balance of that account. So, by wasting a little space, my database file can achieve highest searching performance. Of course there is some overhead to initialize all accounts when database starts its first run but it will only do it once.

c) Since file opening operation is very expensive, my database class allocate each branch file with one RandomAccessFile object to handle file operations.

3. Design of Synchronization

a) Originally I mistakenly think it is enough to add "synchronized" key word to each method of interface like "open", "deposit", "withdraw", and "balance". Later I realized that it will only guarantee synchronization within that method itself, but there is no synchronization between these methods. For example, even though we are sure there is always only one thread calling "open" method one by one, but when "open" is called, "deposit" can also be called by another thread. So, I use class "Semaphore" to handle synchronization.

b) Between ¡°acquire¡± and ¡°release¡± method of Semaphore, there is possible exception throwing and in order to make server robust I force ¡°release¡± at ¡°finally¡± part of ¡°try¡catch¡± statement.

c) Since my synchronization is at level of file and each branch has its own database file, concurrence of operation between different branch is guaranteed.

4. Design of Exception

a) My exception ¡°BankException¡± extends from ¡°RemoteException¡± and mainly acts as a message displaying at client-side. That is whatever error or exception will be displayed at client-side. i.e. withdraw operation with unopened account or withdraw amount exceeds balance of the account will all throw this exception.

b) All other system exception at server-side will be re-thrown when intercepted.

5. Design of Testing

a) Manual testing is made by an interactive menu at client side so that all methods in interface will be tested by command line.

b) Automated testing is made by a loop of 3000 random actions of all methods in interface and user can even run more than one client process at the same time.

c) In automated testing, there is an overflow of port number and connection of RMI is lost.

d) In order to test mutual exclusion cases, I have designed a thread testing method which creates equal number of threads either depositing or withdrawing same amount from same account concurrently. If the server is not properly synchronized with deposit and withdraw operations, obviously there is a very big opportunity that the final balance would not be the same as that before testing. For example, in my testing, I created twenty depositing and twenty withdrawing threads to deposit and withdraw respectively 50 from the same account. And in order to allow them to start at the same time, I used a "CyclicBarrier" to barrier them until all forty threads are created. After the testing, the balance remains exactly same which proves that my server is properly synchronized.

¡¡

    D.The major functions

    Q:

Hi,

1. As for the question I asked you the other day, what is the difference of UDP and TCP in the size of packet? 2. In Java, is static method thread safe? For example, class Float has a method "static float parseFloat(String s);" which is static. And if in my class multi-thread may call this method, is there any problem for this static method? Because I remember in Linux, static API has this kind of thread problem. Can you justify this?

thank you for your time,

¡¡

    A: Unfortunately I lost this important email and I just write down by my memory as a brief account.

    1. The packet size of UDP and TCP is depending on the network environment and should be no difference to lower level. That is to say, the only

    difference of two protocol is whether there is connection or not.

    2. Static method will cause trouble for thread unless it has global variable otherwise those temporary variable is stored in its independent

    stack which will not be affected by context switch. As for the share code, it is just an instruction pointer which is read only.

    However, I restored the message from "Google Desktop Search"! What amazing!

    A:

    1.

    As I can see from the references, the packet size is decided by the
    actual network environment. For example, in Ethernet, there is a upper
    bound of the packet payload, which is 1500 bytes. No matter if UDP or
    TCP is used, it has to follow this upper bound in packet formation and
    transportation. The difference between UDP and TCP is,
    the latter guarantees delivery as well as ordering of packet arrival,
    while the former doesn't.

    2.

    In Java, there is no global variable or global functions. However, Java
    does provide ways to do this kind of job, which is the particular use
    of the keyword "static" for variables and methods. When specified as
    static, a variable/function is globally accessable by other outside
    classes using the classname.

    Therefore, as a global function, a static method can be called anywhere
    without constructing a object of its class. Consequently, it is ok for
    multiple threads to call the same static method, because then it is
    called, its local variables (i.e. those defined within the method) will
    be created temporarily and pushed into local stack, therefore won't
    conflict other copies called by another thread. However, if there is a
    "static" variable within a static method, that variable has only one
    single copy therefore might cause trouble when accessed by multiple
    threads simultaneously.

    For those static methods provided in Java language package, there is no
    such concern of multi-thread data race. However, if you are to define
    your own static method, make sure you do not use static variable in
    that method, or you have to implement mutual exclusion on that variable.

    Q:

    Thanks for your explanation which is clear and convincing!
    I have run into another problem. In RMI, I want to pass parameter by reference. That is to say, to make parameter as "out" parameter. At

    beginning, I thought a class object is enough. Then I realized it must implement "Serializable" interface. So, I try to use "String" which

    implements the interface. However, it doesn't work. It only pass parameter by value, or by copy. I read a little explanation in "sun.com"

    and get an impression that I need to pass by "RemoteObject". Is it true?  If yes, how to create my own "remoteObject"?  By inheritance from

    which one?

    A:

    I think you can just take a look at the RMI example code given by the
    course homepage, as follows:

    http://www.cs.concordia.ca/~comp6231_4/CodeSample/RMI/hello/

    The example given by sun.com is complicated because they are trying to
    demonstrate advanced features called dynamic code loading. Also it is a
    complicated application by itself. The example "Hello" is much simpler
    and easier to understand.

    Regarding the problem you raised, I think there is some confusion here,
    which should be made clear. First, if you try to call a remote method
    whose implementation is residing in a remote object (i.e. the server
    side), the only way to pass parameters is to pass either (a copy of)
    the value (for primary data types like integer, float, ...) or another
    copy (for objects). You cannot pass a reference because it is actually
    pointing to a local object (i.e. on the client side) which is not
    accessable by the remote object (running on the server side). Just
    imagine, these two (client and server) could be running on different
    machines! How could the client tell the server to find out its local
    object and do something with it?

    However, if you claim your local object is accessable globally, then it
    becomes a remote object from the perspective of the server. In other
    words, the server can then allocate it just like the client allocating
    the server's remote object. Certainly you need to the same thing for it,
    such as claiming a remote interface, and having the client's object to
    implement that interface and so on. If you read into the example given
    by sun.com, you will find it is working like this. There are actually
    two remote objects implementing two remote interfaces: one is
    ComputeEngine implementing interface Compute and is running on the
    server side, hence is a remote object accessable by the client, and the
    other is class Pi implementing interface Task and is running on the
    client side, hence is a remote object accessable by the server. The
    latter one is the thing I am talking about regarding your problem.

    I don't know about your design, but I don't think you have to follow
    the sun.com example and make it that complicated. A simple
    implementation like Hello will do, which is what I am suggesting.

    ¡¡

    Q & A:

    > hi,
    >
    > Thanks for your detailed message.
    > 1. For this particular assignment, there is of course simple
    > solution. However, my concern is originated from observation of IDL
    > which defines each parameter either as "in", "out", or "inout". In
    > c++, we have a one-to-one correspondance of translatation to either
    > passing by value, or by reference. However, I cannot see any easy way
    > to translate them into java unless using the way described by you as
    > "RemoteObject" which is not intuitive to programmer at all. By the
    > way, how to compile IDL into java stub class? Or what is the IDL
    > compiler in java?

    Java is different from C++. Therefore what I can suggest is, don't try
    to put your mind of C++ programming into a Java design and try to look
    for the correspondence. Just use what-ever you can find in Java. If you
    want to pass something back (i.e. "out" in C++), use the return
    statement. To resolve your non-intuitive feeling here, you have to
    understand they are REMOATE objects rather than local ones, hence there
    is no way to make local references just like you did in C++.

    Java IDL compiler is idltojava, or in up-to-date versions, idlj. The
    link of this is as follows:

    http://java.sun.com/docs/books/tutorial/idl/hello/idltojava.html

    > 2. I tried to implement thread synchronization by using "Semaphore"
    > in java. However, there is one problem here. See following:
    > Semaphore availabel;
    > ...
    > try{
    >    available.acquire();
    >    ...//doing my work here with possible exception
    >    available.release();
    > }
    > catch(Exception er){
    >     ...//handle exception
    > }
    > finally{
    >    available.release(); //here is the problem
    > }
    >
    > In order to make my server robust, I need to force any thread
    > "finally" release the counted Semaphore at block "finally". This
    > seems OK. However, in sun.com, it is said that "release()" can throw
    > "InterruptedException". This will create an infinite loop. Even
    > though I can simply use an "if (!(er instanceof InterruptException))
    > available.release();" to differenciate this situation, still I am not
    > sure this is the right way for general exception handling. What is
    > your suggestion? Thanks.

    In this situation, I don't think you have to guarantee release in case
    of exceptions. This is rather a design issue. If you do catch
    exceptions, the first thing is to handle it properly, and in some cases
    it might be impossible to continue the release (depends on the nature
    of exception). On the other hand, you might have a boolean indicator of
    exception and turn it on in your catch block, so that in your finally
    block you can check it before release. This possibly avoids loop.

    > 3. As this is first time for me to use java to write program, I need
    > you a little help on this question. However, it is rather a design
    > decision than a technique question. As you see, in all interface we
    > can return a value to indicate success or failure of this operation.
    > (i.e. boolean value like that in "DCOM".) However, remote exception
    > gives an alternative. My question is what is usual way to do it? By
    > using a return boolean value to indicate success or failure of
    > interface or using remote exception which I suspect to be very
    > expensive, right?

    Certainly not. Remote object as return parameter is complicated in both
    design and implementation, and is difficult to debug/test. If you are
    familar or experienced with these stuffs, you might give an advanced
    design with such things, otherwise my suggestion is, make it as simple
    as you can, as long as it works.

    > 4. Another insane observation is that when I declare a variable
    > "float f =100.0;", javac always gives error message "expect float
    > find double" which puzzles me a lot. What is going on here?

    By default such a statement is a claim of double. To claim a float, you
    might put it like this: "float f = 100.0f". In other words, you might
    explicitly indicate you have a float value.

    > 5. And this last question is quite tough for me. As I did automated
    > testing with three clients which try random open, deposit,
    > withdraw,balance operation in a long loop simultaneously, I found out
    > that sooner or later all three clients will lose connection to server
    > by giving exception message of "java.net.BindException: Address
    > already in use: connect". What I can guess is that there is some kind
    > of deadlock happened in server or connection. Is there any clue of
    > this kind of problem?  I attach my code as a reference.
    > explanation:
    > a) three packages: BankServer, BankInterface, BankClient
    > b) to run server: java BankServer.Server
    >    to run client: java BankClient.Client localhost auto
    >    //for client I designed to give both interactive human testing and
    > automated testing, the parameter is "host" "runningchoice".
    > c) There is one database file for each branch and in order to random
    > access balance for each account, I treat file as a big array with
    > length of "sizeof(float)*TotalAccountNumber". At first run, server
    > creates files and initializes all location to -1 to indicate unopened
    > account. When open an account, use randomAccessFile operation "seek"
    > to go to offset of "account*sizeof(float)" and initialize datat to 0.
    > Deposit and withdraw also "seek" to offset of "account*sizeof(float)"
    > and write new balance.

    I took a glance of your code and I am not seeing problems. This is
    actually a performance problem: you are out of TCP/IP connections on
    your machine(s). Taking the fact that you are running a test of 3000
    connections for each client, it is understandable that all the
    available ports will be consumed up soon after you launch the
    application. So it is not your fault.

    My suggestion for testing mutual exclusion is, create several test
    cases where mutual exclusion does happen. So you can demonstrate how
    your solusion works in order to resolve such problem.

    Besides the theory assignment is a bit of interesting since it reveals the necessity of asynchronized RPC.

    2. Remote Procedure Call
    A client makes remote procedure calls to a server. The client takes 5 ms to compute the arguments for each request, and the server takes 10 ms to process each request. The local operating system processing time for each send and receive operation is 0.5 ms, and the network time to transmit each request or reply message is 3 ms. Marshalling or demarshalling takes 0.5 ms per message.
    (a) [10%] Ignoring context switching time, calculate the time taken by the client to generate and return from two requests (i) if it single-threaded, and (ii) if it has two threads that can make requests concurrently on a single processor.
    (b) [5%] Is there a need for asynchronous RPC if client and server processes are threaded? Why or why not?

1. a) Let's label each action in two requests operation as following:

a1) client computing arguments of first request = 5ms

b1) marshalling taken at client-side of first request=0.5ms

c1) os processing time at client-side for sending first request=0.5ms

d1) network transmitting time for first request=3ms

e1) os processing time at server-side for receiving first request=0.5ms

f1) demarshalling for first request at server-side=0.5ms

g1) server processing first request=10ms

h1) marshalling first reply at server-side =0.5ms

i1) os processing time for sending first reply at server-side=0.5ms

j1) network transmitting time for first reply=3ms

k1) os processing time for receiving first reply at client=0.5ms

l1) demarshalling at client-side for first reply=0.5ms

a2) client computing arguments of second request = 5ms

b2) marshalling taken at client-side of second request=0.5ms

c2) os processing time at client-side for sending second request=0.5ms

d2) network transmitting time for second request=3ms

e2) os processing time at server-side for receiving second request=0.5ms

f2) demarshalling for second request at server-side=0.5ms

g2) server processing second request=10ms

h2) marshalling second reply at server-side =0.5ms

i2) os processing time for sending second reply at server-side=0.5ms

j2) network transmitting time for second reply=3ms

k2) os processing time for receiving second reply at client=0.5ms

l2) demarshalling at client-side for second reply=0.5ms

i) If single thread and synchronized RPC, the total time for two request is just

two time of single request. The first request takes total time of a1+b1+c1+d1+e1+f1+g1+h1+i1+j1+k1+l1

=5+0.5+0.5+3+0.5+0.5+10+0.5+0.5+3+ 0.5+0.5

=25ms

So, the two requests takes total 25x2=50ms

ii) For two threads, we can imagine one thread finishes all its job until there is context switch which is the best situation. (because if the second thread switch in before first thread finishes its request sending will only make total time longer. )

client | a1 | b1 | c1 | a2 | b2 | c2 |  |  |  |  | k1 | l1 |  |  |  | k2 | l2
time | 5 | 0.5 | 0.5 | 5 | 0.5 | 0.5 |  |  |  |  | 0.5 | 0.5 |  |  |  | 0.5 | 0.5
server |  |  |  |  | e1 | f1 | g1 | h1 | i1 | e2 | f2 | g2 | h2 | i2 |  |  |
time |  |  |  |  | 0.5 | 0.5 | 10 | 0.5 | 0.5 | 0.5 | 0.5 | 10 | 0.5 | 0.5 |  |  |
network |  |  |  | d1 |  |  |  |  |  | j1 |  |  |  |  | j2 |  |
time |  |  |  | 3 |  |  |  |  |  | 3 |  |  |  |  | 3 |  |

total time

= a1+b1+c1+d1+e1+f1+g1+h1+i1+e2+f2+g2+h2+i2+j2+k2+l2

=5+0.5+0.5+3+0.5+0.5+10+0.5+0.5+0.5+0.5+10+0.5+0.5+3+0.5+0.5

=37ms

So, the total time for two threads for two requests is 37ms.

b)

Yes, there is need for asynchronized RPC because we can view question ii of a) as asynchronized RPC as at both client and server all jobs are done sequentially. That is to say, if in asynchronized RPC for single thread to process two requests, the total time is 37ms instead of 50ms in synchronized RPC in question i) of a). The speedup is (50-37)/50=26% which is quite significant.

{According to solution, this conclusion should be opposite because by using multi-thread model we achieve same

effect of asynchronization. Then why do we need it?}

    E.Further improvement

    RMI is implemented by TCP and the automated testing which use a loop of 3000 requests to run the client will cause TCP

    connection breakdown. The tutor suggested to design some mutual exclusive cases to test. However, I can only think about

    creating some threads to try to access same data which is even more complicated than the current code. So, I give it up.

    To Compile:

    1. setup classpath to your working directory.

    2. create BankInterface, BankClient, BankServer directory in working directory.

    3. place source files into directory according to its package (directory name).

    4. at command line,

    5. to compile server: javac .\BankServer\Server.java

    6. to compile client: javac .\BankClient\Client.java

    To Run:

    to run server: java BankServer.Server

    to run client for interactive testing: java BankClient.Client localhost manual

    to run client for automated testing: java BankClient.Client localhost automated

    to run client for thread testing:    java BankClient.Client localhost t

    ¡¡

    ¡¡

    { The buggy part of previous version is quite a mixed style. First there is some subtle thing about "static" and non-static.

    If you are going to synchronize, you have to make something static such as the global file pointer, global semaphore. So,

    for you have to declare those in your "implementation class". This is the first mistake I made. And I removed the wrapper

    class "database" for debugging. And the major mistake is my misunderstanding of java syntax of "finally" which will be

    executed no matter whether there is exception or not. In order to maintain a correct state for a robust server, at beginning

    I add extra "semaphore release" statement in "finally" block which doubles the "release count" of semaphore. This of course

    invalidate functionality of semaphore.}

    ¡¡

    ¡¡

    F.File listing

    ¡¡

    package BankInterface:

    1. BankInterface.java

    2. BankException.java

    ¡¡

    package BankClient:

    1. BankClient.java

    ¡¡

    package BankServer:

    1. BankServer.java

    2. BankInterfaceImplement.java

    file name: BankInterface.java

    package BankInterface;

    import java.rmi.*;

    public interface BankInterface extends Remote {

    	public final int RMIPort=1099;
    	public static final int BranchIDOffset=100;
    	public static final int MaxBranchNumber=1;
    	//the contact maybe telephone number

    	public int Open()
    		throws java.rmi.RemoteException;

    	public float Deposit(int acnt, float amt)
    		throws java.rmi.RemoteException;

    	public float Withdraw(int acnt, float amt)
    		throws java.rmi.RemoteException;

    	//amt out
    	public float Balance(int acnt)
    		throws java.rmi.RemoteException;

    } //end interface

    ¡¡

    file name: BankException.java

    package BankInterface;

    import java.rmi.*;

    public class BankException extends java.rmi.RemoteException
    {
    	public BankException()
    	{
    		super();
    	}
    	public BankException(String msg)
    	{
    		super(msg);
    	}
    }

    file name: Client.java

    package BankClient;

    import java.io.*;
    import java.rmi.*;
    import java.util.*;
    import java.util.concurrent.*;
    import java.rmi.registry.Registry;
    import java.rmi.registry.LocateRegistry;

    import BankInterface.*;

    public class Client
    {

    	//protected static DataOutputStream bufOutput;//= new DataOutputStream(System.out);

    	protected static int RunningThreadNumber=200;
    	protected static RunningThread deposits[];
    	protected static RunningThread withdraws[];
    	protected static CyclicBarrier barrier=new CyclicBarrier(RunningThreadNumber*2);

    	protected static BufferedReader bufInput;//= new BufferedReader(System.in);
    	//protected static DataInputStream bufInput= new DataInputStream(new InputStreamReader(System.in));

    	protected static final String menuStr
    		="Please input your choice:\n Open(1), Deposit(2), Withdraw(3), Balance(4), Exit(5)\n";

    	protected static BankInterface h;

    	public static void main(String args[])
    	{
    		try
    		{
    			String hostName;
    			String portNum;
    			String registryURL;

    			//bufOutput=new DataOutputStream(System.out);
    			bufInput = new BufferedReader(new InputStreamReader(System.in));

    			//the first argument must be machine name
    			if (args.length>0)
    			{
    				hostName=args[0];
    			}
    			else
    			{
    				hostName="localhost";
    			}
    			portNum=Integer.toString(BankInterface.RMIPort);

    			registryURL = "rmi://"+hostName +":" + portNum + "/BankInterface";
    			h =	(BankInterface)Naming.lookup(registryURL);

    			// invoke the remote method(s)
    			if (args.length>0&&args[1].charAt(0)=='a')
    			{
    				autoTester();
    			}
    			else
    			{
    				if (args.length>0&&args[1].charAt(0)=='t')
    				{
    					threadTester();
    				}
    				else
    				{
    					manualTester();
    				}
    			}

    		} // end try
    		catch (Exception ex)
    		{
    			//ex.printStackTrace();
    			System.out.println(ex.getMessage());
    		}
    	}

    	//a helper class of thread to test mutual exclusion in server
    	static class RunningThread extends Thread
    	{
    		protected float amount, newAmount;
    		protected int account;
    		//protected BankInterface bankInterface;
    		public RunningThread(int acnt,  float amt)
    		{
    			amount=amt;
    			account=acnt;
    		}

    		public void run()
    		{
    			try
    			{
    				//System.out.println(Integer.toString(barrier.getNumberWaiting()));
    				barrier.await();
    				if (amount>0)
    				{

    					System.out.println(getName()+ " reports to deposit "+Float.toString(amount) +
    						" to account " + Integer.toString(account) +" and balance is " +
    						Float.toString(h.Deposit(account, amount)));

    					h.Deposit(account, amount);
    				}
    				else
    				{

    					System.out.println(getName()+ " reports to withdraw "+Float.toString(-amount) +
    						" to account " + Integer.toString(account)+ " and balance is " +
    						Float.toString(h.Withdraw(account, -amount)));

    					h.Withdraw(account, -amount);
    				}
    			}
    			catch (Exception er)
    			{
    				System.out.println(er.getMessage());
    			}
    		}
    	}

    	protected static void threadTester()
    	{
    		int account;
    		float amount;
    		try
    		{
    			account=h.Open();//open a new account;
    			//account=1037202;
    			amount=h.Deposit(account, 3000);
    			deposits=new RunningThread[RunningThreadNumber];
    			withdraws=new RunningThread[RunningThreadNumber];
    			System.out.println("thread test begins and we deposit 3000.0 to account "
    				+ Integer.toString(account)
    				+ " and we are going to run equal number of deposit and withdraw actions," +
    				"if server is not properly synchronized, the balance will not be the same probably");

    			System.out.println("the balance of account "+ Integer.toString(account) + " is "
    				+ Float.toString(amount));

    			for (int i=0; i<RunningThreadNumber; i++)
    			{
    				deposits[i]=new RunningThread(account, 50);
    				deposits[i].start();
    				withdraws[i]=new RunningThread(account, -50);
    				withdraws[i].start();
    			}

    			for (int i=0; i<RunningThreadNumber; i++)
    			{
    				deposits[i].join();
    				withdraws[i].join();
    			}

    			System.out.println("thread test is over and let's check if the balance of account remains same as before");

    			System.out.println("the balance of "+Integer.toString(account) + " is " +
    				Float.toString(h.Balance(account)));
    		}
    		catch (Exception er)
    		{
    			System.out.println(er.getMessage());
    		}
    	}

    	protected static void manualTester()
    	{
    		//now do the test
    		int choice=0;
    		int account;
    		float amount;

    		do
    		{
    			//bufOutput.writeBytes(menuStr);
    			System.out.println(menuStr);

    			try
    			{
    				choice = Integer.parseInt(bufInput.readLine());
    				switch(choice)
    				{
    					case 1:
    						account=h.Open();
    						//bufOutput.writeBytes("new opened account is" + Integer.toString(account)+"\n");
    						System.out.println("new opened account is " + Integer.toString(account));
    						break;
    					case 2:
    						//bufOutput.writeBytes("input account no.\n");
    						System.out.println("input account no.");
    						account=Integer.parseInt(bufInput.readLine());
    						//bufOutput.writeBytes("input amount to deposit:\n");
    						System.out.println("input amount to deposit:");
    						amount=Float.parseFloat(bufInput.readLine());
    						System.out.println("new balance is "+Float.toString(h.Deposit(account, amount)));
    						break;
    					case 3:
    						//bufOutput.writeBytes("input account no.\n");
    						System.out.println("input account no");
    						account=Integer.parseInt(bufInput.readLine());
    						//bufOutput.writeBytes("input amount to withdraw:\n");
    						System.out.println("input amount to withdraw:");
    						amount=Float.parseFloat(bufInput.readLine());
    						System.out.println("new balance is "+ Float.toString(h.Withdraw(account, amount)));
    						break;
    					case 4:
    						//bufOutput.writeBytes("input account no.\n");
    						System.out.println("input account no");
    						account=Integer.parseInt(bufInput.readLine());
    						//bufOutput.writeBytes("balance of account " + Integer.toString(account)+
    						//	" is " + Float.toString(h.Balance(account))+ "\n");
    						System.out.println("balance of account " + Integer.toString(account)+
    							" is " + Float.toString(h.Balance(account)));
    						break;
    					case 5:
    						//bufOutput.writeBytes("exiting test\n");
    						System.out.println("exiting test\n");
    						break;
    					default:
    						//bufOutput.writeBytes("please input choice between 1 to 5.\n");
    						System.out.println("please input choice between 1 to 5.");
    						break;
    				}
    			}
    			catch (Exception er)
    			{
    				System.out.println(er.getMessage());
    			}

    		}
    		while (choice!=5);
    	}

    	protected static void autoTester()
    	{
    		final int MaxAmount=1000;
    		Random rand=new Random();
    		int choice, account;
    		float amount;

    		for (int i=0; i<3000; ++i)
    		{
    			try
    			{
    				//purely for testing
    				//choice=0;
    				choice=rand.nextInt(4);
    				switch (choice)
    				{
    				case 0:
    					//open
    					System.out.println("try to open a new account");
    					System.out.println("a new opened account is " +Integer.toString(h.Open()));
    					break;
    				case 1:
    					//deposit
    					account=(rand.nextInt(BankInterface.MaxBranchNumber)+BankInterface.BranchIDOffset)*10000
    						+rand.nextInt(10000);
    					amount=rand.nextInt(MaxAmount)+rand.nextInt(100)/(float)100.0;
    					System.out.println("try to deposit account "+Integer.toString(account)+" amount of "
    						+ Float.toString(amount));

    					System.out.println("account "+Integer.toString(account)+" is deposited with "+Float.toString(amount)
    						+ " and new balance is "+ Float.toString(h.Deposit(account, amount)));
    					break;
    				case 2:
    					//withdraw
    					account=(rand.nextInt(BankInterface.MaxBranchNumber)+BankInterface.BranchIDOffset)*10000
    						+rand.nextInt(10000);
    					//i want to use float with two digits after point
    					amount=rand.nextInt(MaxAmount)+rand.nextInt(100)/(float)100.0;
    					System.out.println("try to withdraw account "+Integer.toString(account)+" amount of "
    						+ Float.toString(amount));

    					System.out.println("account "+Integer.toString(account)+" is withdraw with "+Float.toString(amount)
    						+" and new balance is " + Float.toString(h.Withdraw(account, amount)));
    					break;
    				case 3:
    					//balance
    					account=(rand.nextInt(BankInterface.MaxBranchNumber)+BankInterface.BranchIDOffset)*10000
    						+rand.nextInt(10000);
    					System.out.println("try to get balance of account "+Integer.toString(account));
    					amount=h.Balance(account);
    					System.out.println("account "+Integer.toString(account)+" has balance of "+Float.toString(amount));
    					break;
    				}
    			}
    			catch(Exception er)
    			{
    				System.out.println(er.getMessage());
    			}
    		}
    	}
    }

    ¡¡

    file name: Server.java

    package BankServer;

    import java.rmi.*;
    import java.rmi.server.*;

    import java.rmi.registry.Registry;
    import java.rmi.registry.LocateRegistry;
    import java.net.*;
    import java.io.*;

    import BankInterface.*;

    /**
     * This class represents the object server for a distributed
     * object of class SomeImpl, which implements the remote
     * interface SomeInterface.
     */

    public class Server  {
    	public static void main(String args[])
    	{
    		String portNum = Integer.toString(BankInterface.RMIPort), registryURL;
    		String host;
    		try
    		{
    			// code for obtaining RMI port number value omitted
    			BankInterfaceImplement exportedObj = new BankInterfaceImplement();
    			startRegistry(BankInterface.RMIPort);
    			// register the object under the name "some"
    			if (args.length>0)
    			{
    				host=args[0];
    			}
    			else
    			{
    				ServerSocket serverSocket=new ServerSocket(9000);
    				InetAddress inetAddress=serverSocket.getInetAddress();
    				host=inetAddress.getLocalHost().getHostName();
    				serverSocket.close();
    			}

    			registryURL = "rmi://"+ host+ ":" + portNum + "/BankInterface";
    			Naming.rebind(registryURL, exportedObj);
    			System.out.println("Bank Server ready.");
    		}// end try
    		catch (Exception re)
    		{
    			System.out.println("Exception in BankServer.main: " + re);
    		} // end catch
    	} // end main

       // This method starts a RMI registry on the local host, if it
       // does not already exist at the specified port number.
       private static void startRegistry(int RMIPortNum)
          throws RemoteException
    	{
    		try
    		{
    			Registry registry = LocateRegistry.getRegistry(RMIPortNum);
    			registry.list( );
    			// The above call will throw an exception

    		}// if the registry does not already exist
    		catch (RemoteException ex)
    		{
    			// No valid registry at that port.
    			System.out.println(
    			"RMI registry cannot be located at port "
    			+ RMIPortNum);
    			Registry registry = LocateRegistry.createRegistry(RMIPortNum);
    			System.out.println(
    			"RMI registry created at port " + RMIPortNum);
    		}
       } // end startRegistry

    } // end class

    ¡¡

    file name: BankInterfaceImplement.java

    package BankServer;
    import java.lang.*;
    import java.rmi.*;
    import java.util.*;
    import java.io.*;
    import java.util.concurrent.Semaphore;

    import java.rmi.server.*;
    import BankInterface.*;

    /**
     * This class implements the remote interface BankInterface.
     */

    //the big trap is to declare both branchID and account as a data member of this
    //class, because RMI may invoke multi-thread and these data is not thread safe
    //so, keep them as temporary variable in each function call.
    public class BankInterfaceImplement
    	extends UnicastRemoteObject
    	implements BankInterface
    {
    	protected static final int MAX_AVAILABLE = 1;
    	//protected static DataBase dataBase;
    	protected static Random rand;
    	protected static boolean isFirst=true;
    	public static Semaphore available;

    	//even though we are sure there is only one instance of "DataBase" in server, still to make semanctically clear,
    	//make branchmananger static too.
    	public static BranchManager branches[];

    	//constructor
    	public BankInterfaceImplement() //throws RemoteException
    		throws java.rmi.RemoteException
    	{
    		super();
    		try
    		{
    			if (isFirst)
    			{
    				isFirst=false;
    				//dataBase=new DataBase();
    				rand=new Random();
    				available=new Semaphore(MAX_AVAILABLE, true);

    				branches=new BranchManager[BankInterface.MaxBranchNumber];

    				for (int i=0; i<BankInterface.MaxBranchNumber; i++)
    				{
    					branches[i]=new BranchManager(i);
    				}

    			}
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}
    	public void finalize() throws java.rmi.RemoteException
    	{
    		if (!isFirst)
    		{
    			isFirst=true;//because in constructor, it is already set to be false
    			//dataBase.finalize();
    		}
    	}

    	//the branchID has to be shifted to make 7 digit
       	public int Open() //throws java.io.IOException
    		throws java.rmi.RemoteException
    	{
    		int branchID;
    		int account;
    		try
    		{
    			branchID=rand.nextInt(BankInterface.MaxBranchNumber);
    			account=branches[branchID].Open();
    			return SetBranchID(branchID, account);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public float Deposit(int acnt, float amount) //throws java.io.IOException
    		throws java.rmi.RemoteException
    	{
    		int branchID, account;
    		branchID=GetBranchID(acnt);
    		account=acnt%10000;
    		//check range of branch id

    		//to prevent deposit negative amount
    		if (amount<=0)
    		{
    			throw new BankException("negative amount");
    		}
    		if (!CheckBranchID(branchID))
    		{
    			throw new BankException("invalid branch id");
    		}
    		try
    		{
    			return branches[branchID].Deposit(account, amount);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}

    	}

    	public float Withdraw(int acnt, float amount)  //throws java.io.IOException
    		throws java.rmi.RemoteException
    	{

    		int branchID, account;

    		branchID=GetBranchID(acnt);
    		account=acnt%10000;
    		//check range of branch id
    		//and make sure amount is positive
    		if (amount<=0)
    		{
    			throw new BankException("negative amount");
    		}
    		if (!CheckBranchID(branchID))
    		{
    			throw new BankException("invalid branch id");
    		}

    		try
    		{
    			return branches[branchID].Withdraw(account, amount);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	//amt out this is not synchronized
    	public float Balance(int acnt)  //throws java.io.IOException
    		throws java.rmi.RemoteException
    	{
    		int branchID, account;

    		branchID=GetBranchID(acnt);
    		account=acnt%10000;
    		//check range of branch id

    		if (!CheckBranchID(branchID))
    		{
    			throw new BankException("invalid branch id");
    		}

    		try
    		{
    			return branches[branchID].Balance(account);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	//you may have specific method in future
    	protected boolean CheckBranchID(int branchID)
    	{
    		return (branchID>=0&&branchID<BankInterface.MaxBranchNumber+BankInterface.BranchIDOffset);
    	}
    	protected int SetBranchID(int id, int account)
    	{
    		return (id+BranchIDOffset)*10000 + account;
    	}

    	protected int GetBranchID(int account)
    	{
    		return account/10000-BankInterface.BranchIDOffset;
    	}
    }

    //declare a helper class

    class BranchManager
    {

    	private int branchID;
    	private static final float NegativeInfinite= -10000;
    	private Random rand;
    	//static boolean flag=true;
    	public static final int AccountSize=4;

    	public RandomAccessFile branchFile;

    	public BranchManager(int id)   throws java.rmi.RemoteException
    	{
    		branchID=id;
    		rand=new Random(id);

    		initialize();
    	}

    	public void finalize() throws java.rmi.RemoteException
    	{
    		try
    		{
    			branchFile.close();
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}

    	}

    	void initialize()   throws java.rmi.RemoteException
    	{
    		String fileName=Integer.toString(branchID+1)+".data";
    		try
    		{
    			branchFile=new RandomAccessFile(fileName, "rw");
    			if (branchFile.length()==0)//this is the new file
    			{
    				branchFile.setLength(AccountSize*10000);
    				branchFile.seek(0);//starting
    				for (int j=0; j<10000; j++)
    				{
    					//branchFiles[i].writeInt(0);//initialize to 0;
    					branchFile.writeFloat(NegativeInfinite);// indicating unused
    				}
    			}
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	protected boolean findAccount(int account) throws java.rmi.RemoteException
    	{
    		try
    		{
    			branchFile.seek(account*AccountSize);
    			return branchFile.readFloat()!=NegativeInfinite;
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public int Open()	throws java.rmi.RemoteException
    	{
    		int result;
    		try
    		{
    			//entering critical section
    			//purely for testing
    			//System.out.println("open before acquire");
    			BankInterfaceImplement.available.acquireUninterruptibly();
    			//purely for testing
    			//this is purely for testing
    			if (BankInterfaceImplement.available.availablePermits()>0)
    			{
    				throw new BankException("during open, semaphore is corrupted");
    			}

    			//System.out.println("open after acquire");
    			do
    			{
    				//allow account starting from 0
    				result=rand.nextInt(10000);
    				//System.out.println("now it is "+Integer.toString(result));
    			}
    			while (findAccount(result));
    			branchFile.seek(result*AccountSize);
    			branchFile.writeFloat(0);

    			//purely for testing
    			//System.out.println("open before release new account is " +Integer.toString(result));
    			//BankInterfaceImplement.available.release();
    			//purely for testing
    			//System.out.println("open after release new account is " +Integer.toString(result));
    			//leaving critical section
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    		finally
    		{
    			BankInterfaceImplement.available.release();
    		}
    		return result;
    	}

    	public float Deposit(int account, float amount) throws java.rmi.RemoteException
    	{

    		//entering critical section
    		try
    		{
    			float oldValue;

    			BankInterfaceImplement.available.acquireUninterruptibly();
    			//this is purely for testing
    			if (BankInterfaceImplement.available.availablePermits()>0)
    			{
    				throw new BankException("during deposit, semaphore is corrupted");
    			}
    			branchFile.seek(account*AccountSize);
    			oldValue=branchFile.readFloat();
    			//account not opened
    			if (oldValue==NegativeInfinite)
    			{
    				//BankInterfaceImplement.available.release();
    				throw new BankException("during deposit, account is not opened");
    			}
    			else
    			{
    				//it is pity there is no backward moving
    				branchFile.seek(account*AccountSize);
    				branchFile.writeFloat(amount+oldValue);
    			}

    			//BankInterfaceImplement.available.release();
    			return amount+oldValue;
    			//leaving critical section
    		}
    		catch(Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    		finally
    		{
    			BankInterfaceImplement.available.release();
    		}

    	}

    	//assume amount is positive
    	public float Withdraw(int account, float amount) throws java.rmi.RemoteException
    	{
    		try
    		{
    			float oldValue=NegativeInfinite;
    			//entering critical section
    			BankInterfaceImplement.available.acquireUninterruptibly();

    			//this is purely for testing
    			if (BankInterfaceImplement.available.availablePermits()>0)
    			{
    				throw new BankException("during withdraw, semaphore is corrupted");
    			}

    			branchFile.seek(account*AccountSize);
    			oldValue=branchFile.readFloat();
    			//account cannot have negative balance
    			//including the case of unopened account
    			if (oldValue==NegativeInfinite)
    			{
    				throw new BankException("during withdraw, account is not opened");
    			}

    			//it is pity there is no backward moving
    			branchFile.seek(account*AccountSize);
    			branchFile.writeFloat(oldValue-amount);

    			//BankInterfaceImplement.available.release();
    			//leaving critical section
    			return oldValue-amount;

    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    		finally
    		{
    			BankInterfaceImplement.available.release();
    		}
    	}

    	//even reading may not need to be synchronized, but
    	//file pointer has to be! So make it simple by synchronized!
    	public float Balance(int account)   throws java.rmi.RemoteException
    	{
    		float oldValue;
    		try
    		{
    			//entering critical section
    			BankInterfaceImplement.available.acquireUninterruptibly();
    			//this is purely for testing
    			if (BankInterfaceImplement.available.availablePermits()>0)
    			{
    				throw new BankException("during balance, semaphore is corrupted");
    			}

    			branchFile.seek(account*AccountSize);
    			oldValue=branchFile.readFloat();

    			if (oldValue==NegativeInfinite)
    			{
    				throw new BankException("druing open, account has not opened");
    			}
    			return oldValue;
    			//leaving critical section
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    		finally
    		{
    			BankInterfaceImplement.available.release();

    		}

    	}
    }

    /*
    class DataBase
    {
    	public static final int AccountSize=4;
    	//even though we are sure there is only one instance of "DataBase" in server, still to make semanctically clear,
    	//make branchmananger static too.
    	public static BranchManager branches[];

    	public DataBase()  throws java.rmi.RemoteException
    	{
    		try
    		{
    			branches=new BranchManager[BankInterface.MaxBranchNumber];

    			for (int i=0; i<BankInterface.MaxBranchNumber; i++)
    			{
    				branches[i]=new BranchManager(i);
    			}
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public void finalize() throws java.rmi.RemoteException
    	{
    		try
    		{
    			for (int i=0; i<BankInterface.MaxBranchNumber; i++)
    			{
    				branches[i].finalize();
    			}
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public int Open(int branchID) throws java.rmi.RemoteException
    	{
    		try
    		{
    			return branches[branchID].Open();
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public float Deposit(int branchID, int account, float amount) throws java.rmi.RemoteException
    	{
    		try
    		{
    			return branches[branchID].Deposit(account, amount);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public float Withdraw(int branchID, int account, float amount) throws java.rmi.RemoteException
    	{
    		try
    		{
    			return branches[branchID].Withdraw(account, amount);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}

    	public float Balance(int branchID, int account) throws java.rmi.RemoteException
    	{
    		try
    		{
    			return branches[branchID].Balance(account);
    		}
    		catch (Exception er)
    		{
    			throw new BankException(er.getMessage());
    		}
    	}
    }

    */

    A snapshot of running automated testing:

¡.

try to get balance of account 1006177

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account has not opened

try to deposit account 1018599 amount of 953.47

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account is not opened

try to withdraw account 1026943 amount of 462.89

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account is not opened or balance is less than withdraw amount

try to get balance of account 1012449

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account has not opened

try to open a new account

a new opened account is 1017130

try to open a new account

a new opened account is 1082668

try to withdraw account 1014824 amount of 308.14

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account is not opened or balance is less than withdraw amount

try to get balance of account 1085843

RemoteException occurred in server thread; nested exception is:

BankInterface.BankException: account has not opened

try to open a new account

a new opened account is 1007025

¡..

A snapshot of running thread testing:

J:\MyJava>java BankClient.Client localhost t
thread test begins and we deposit 3000.0 to account 1078696 and we are going to
run equal number of deposit and withdraw actions,if server is not properly synch
ronized, the balance will not be the same probably
the balance of account 1078696 is 3000.0
Thread-2 reports to deposit 50.0 to account 1078696
Thread-9 successfully withdraw 50.0 to account 1078696
Thread-13 successfully withdraw 50.0 to account 1078696
Thread-32 reports to deposit 50.0 to account 1078696
Thread-33 successfully withdraw 50.0 to account 1078696
Thread-34 reports to deposit 50.0 to account 1078696
Thread-11 successfully withdraw 50.0 to account 1078696
Thread-3 successfully withdraw 50.0 to account 1078696
Thread-14 reports to deposit 50.0 to account 1078696
Thread-4 reports to deposit 50.0 to account 1078696
Thread-5 successfully withdraw 50.0 to account 1078696
Thread-6 reports to deposit 50.0 to account 1078696
Thread-36 reports to deposit 50.0 to account 1078696
Thread-37 successfully withdraw 50.0 to account 1078696
Thread-15 successfully withdraw 50.0 to account 1078696
Thread-38 reports to deposit 50.0 to account 1078696
Thread-16 reports to deposit 50.0 to account 1078696
Thread-39 successfully withdraw 50.0 to account 1078696
Thread-41 successfully withdraw 50.0 to account 1078696
Thread-40 reports to deposit 50.0 to account 1078696
Thread-17 successfully withdraw 50.0 to account 1078696
Thread-35 successfully withdraw 50.0 to account 1078696
Thread-7 successfully withdraw 50.0 to account 1078696
Thread-8 reports to deposit 50.0 to account 1078696
Thread-18 reports to deposit 50.0 to account 1078696
Thread-19 successfully withdraw 50.0 to account 1078696
Thread-22 reports to deposit 50.0 to account 1078696
Thread-26 reports to deposit 50.0 to account 1078696
Thread-2 reports the balance of account 1078696 is 3000.0
Thread-27 successfully withdraw 50.0 to account 1078696
Thread-28 reports to deposit 50.0 to account 1078696
Thread-29 successfully withdraw 50.0 to account 1078696
Thread-34 reports the balance of account 1078696 is 2950.0
Thread-32 reports the balance of account 1078696 is 2950.0
Thread-13 reports the balance of account 1078696 is 2950.0
Thread-9 reports the balance of account 1078696 is 2950.0
Thread-30 reports to deposit 50.0 to account 1078696
Thread-10 reports to deposit 50.0 to account 1078696
Thread-12 reports to deposit 50.0 to account 1078696
Thread-3 reports the balance of account 1078696 is 3100.0
Thread-4 reports the balance of account 1078696 is 3100.0
Thread-5 reports the balance of account 1078696 is 3100.0
Thread-6 reports the balance of account 1078696 is 3100.0
Thread-36 reports the balance of account 1078696 is 3100.0
Thread-15 reports the balance of account 1078696 is 3100.0
Thread-38 reports the balance of account 1078696 is 3100.0
Thread-39 reports the balance of account 1078696 is 3100.0
Thread-41 reports the balance of account 1078696 is 3100.0
Thread-40 reports the balance of account 1078696 is 3100.0
Thread-7 reports the balance of account 1078696 is 3100.0
Thread-8 reports the balance of account 1078696 is 3100.0
Thread-18 reports the balance of account 1078696 is 3100.0
Thread-19 reports the balance of account 1078696 is 3100.0
Thread-26 reports the balance of account 1078696 is 3100.0
Thread-28 reports the balance of account 1078696 is 3100.0
Thread-14 reports the balance of account 1078696 is 3100.0
Thread-33 reports the balance of account 1078696 is 2950.0
Thread-29 reports the balance of account 1078696 is 3100.0
Thread-35 reports the balance of account 1078696 is 3100.0
Thread-27 reports the balance of account 1078696 is 3100.0
Thread-16 reports the balance of account 1078696 is 3100.0
Thread-22 reports the balance of account 1078696 is 3100.0
Thread-30 reports the balance of account 1078696 is 3100.0
Thread-10 reports the balance of account 1078696 is 3100.0
Thread-37 reports the balance of account 1078696 is 3100.0
Thread-25 successfully withdraw 50.0 to account 1078696
Thread-11 reports the balance of account 1078696 is 3050.0
Thread-17 reports the balance of account 1078696 is 3050.0
Thread-23 successfully withdraw 50.0 to account 1078696
Thread-24 reports to deposit 50.0 to account 1078696
Thread-12 reports the balance of account 1078696 is 3050.0
Thread-31 successfully withdraw 50.0 to account 1078696
Thread-21 successfully withdraw 50.0 to account 1078696
Thread-20 reports to deposit 50.0 to account 1078696
Thread-25 reports the balance of account 1078696 is 3000.0
Thread-31 reports the balance of account 1078696 is 3000.0
Thread-23 reports the balance of account 1078696 is 3000.0
Thread-24 reports the balance of account 1078696 is 3000.0
Thread-20 reports the balance of account 1078696 is 3000.0
Thread-21 reports the balance of account 1078696 is 3000.0
thread test is over and let's check if the balance of account remains same as be
fore
the balance of 1078696 is 3000.0
J:\MyJava>

¡¡

    ¡¡