简体   繁体   中英

Java RMI server side threading

I'm just getting started with RMI and I'm trying to write a simple program that simulates a train booking system. I have the basics set up - Server, Client, and a Remote object exported. It works fine with one Client connection. However when more than 1 Client connects, the Clients seem to be executing in the same thread. This is the case when I run multiple Clients on the same machine or when I connect a Client from another laptop.

I was under the impression that RMI handled threading on the server side? If not, how do I go about handling multiple Client connections given the code below?

Here are the classes of interest.

Server.....

public class Server {

    public Server() {
        try {
            Booking stub = (Booking) UnicastRemoteObject.exportObject(new BookingProcess(), 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.bind("Booking", stub);
            System.err.println("Server Ready");
        } catch (RemoteException e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
    }

}

BookingProcess.....(I've left out the private methods that processInput(String input) uses)

public class BookingProcess implements Booking {

    private static Journey dublinGalway = new Journey("Dublin to Galway");
    private static Journey dublinLimerick = new Journey("Dublin to Limerick");
    private static Journey dublinCork = new Journey("Dublin to Cork");
    private Journey currentJourney;

    private enum State {
        INITIAL, JOURNEYS_DISPLAYED, JOURNEY_CHOSEN, ANOTHER_BOOKING_OFFERED, SOLD_OUT;
    }

    private State currentState = State.INITIAL;

    public synchronized String processInput(String input) {
        String output = "";

        if(currentState == State.INITIAL) {
            if(bookedOut()) {
                output = "Sorry, there are no seats remaining on any route. Get the bus.";
                currentState = State.SOLD_OUT;
            }
            else {
                output = "Please choose a journey to book: " + "1: " + dublinGalway.getDescription() + ", 2: " + dublinLimerick.getDescription() + ", 3: " + dublinCork.getDescription();
                currentState = State.JOURNEYS_DISPLAYED;
            }
        }

        else if(currentState == State.JOURNEYS_DISPLAYED) {
            output = this.processJourneyChoice(input);
        }

        else if(currentState == State.JOURNEY_CHOSEN) {
            output = "Do you wish to confirm this booking? (y/n)";
            if(input.equalsIgnoreCase("y")) {
                if(bookingConfirmed()) {
                    output = "Thank you. Your journey from " + currentJourney.getDescription() + " is confirmed. Hit return to continue.";
                    //currentState = State.ANOTHER_BOOKING_OFFERED;
                }
                else {
                    output = "Sorry, but the last seat on the " + currentJourney.getDescription() + " route has just been booked by another user.";
                    //currentState = State.ANOTHER_BOOKING_OFFERED;
                }
                currentState = State.ANOTHER_BOOKING_OFFERED;
            }
            else if(input.equalsIgnoreCase("n")) {
                output = "You have cancelled this booking. Hit return to continue.";
                currentState = State.ANOTHER_BOOKING_OFFERED;
            }
        }

        else if(currentState == State.ANOTHER_BOOKING_OFFERED) {
            output = "Would you like to make another booking? (y/n)";
            if(input.equalsIgnoreCase("y")) {
                output = "Hit Return to continue.";
                currentState = State.INITIAL;
            }
            else if(input.equalsIgnoreCase("n")){
                output = "Goodbye.";
                try {
                    Thread.currentThread().join(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                currentState = State.INITIAL;
            }
        }

        else if(currentState == State.SOLD_OUT) {
            output = "Goodbye.";
        }

        return output;
    }

And finally Client......

public class Client {

    public static void main(String[] args) {
        Client client = new Client();
        client.runClient();
    }

    public void runClient() {

        try {
            BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));
            Registry registry = LocateRegistry.getRegistry("localhost");
            Booking stub = (Booking) registry.lookup("Booking");
            String serverResponse = stub.processInput("begin");
            System.out.println("Server: " + serverResponse);

            while((serverResponse = stub.processInput(consoleInput.readLine())) != null) {
                 System.out.println(serverResponse);
                 if(serverResponse.equals("Goodbye.")) {
                        break;
                 }
            }
        } catch (Exception e) {
            System.err.println("Client exception " + e.toString());
            e.printStackTrace();
        }
    }


}

As for as RMI server threads, the answer is that it may or may not run in a separate thread. See the documentation here:

http://docs.oracle.com/javase/6/docs/platform/rmi/spec/rmi-arch3.html

3.2 Thread Usage in Remote Method Invocations

A method dispatched by the RMI runtime to a remote object implementation may or may not execute in a separate thread. The RMI runtime makes no guarantees with respect to mapping remote object invocations to threads. Since remote method invocation on the same remote object may execute concurrently, a remote object implementation needs to make sure its implementation is thread-safe.

You can take server side thread dumps and you would see that the RMI TCP Connection threads IDs keep changing, however as @jtahlborn noticed the server side method is synchronized so it would execute serially, not necessarily in a single thread though.

Your server side processInput() method is synchronized, so, yes, the calls will be handled serially. what does that have to do with RMI?

UPDATE:

if you want to have separate currentState and currentJourney values for each client session , then you need to use the RMI remote session pattern, see this answer for details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM