3

我刚刚开始使用 RMI,我正在尝试编写一个模拟火车预订系统的简单程序。我已经设置了基础 - 服务器、客户端和导出的远程对象。它适用于一个客户端连接。但是,当超过 1 个客户端连接时,客户端似乎在同一个线程中执行。当我在同一台机器上运行多个客户端或从另一台笔记本电脑连接客户端时就是这种情况。

我的印象是 RMI 在服务器端处理线程?如果没有,我该如何处理给定以下代码的多个客户端连接?

以下是感兴趣的课程。

服务器.....

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 .....(我省略了 processInput(String input) 使用的私有方法)

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;
    }

最后是客户端......

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();
        }
    }


}
4

2 回答 2

4

至于 RMI 服务器线程,答案是它可能会或可能不会在单独的线程中运行。请参阅此处的文档:

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

3.2 远程方法调用中的线程使用

由 RMI 运行时分派给远程对象实现的方法可能会也可能不会在单独的线程中执行。RMI 运行时不保证将远程对象调用映射到线程。由于对同一个远程对象的远程方法调用可能同时执行,远程对象实现需要确保其实现是线程安全的。

您可以进行服务器端线程转储,并且您会看到 RMI TCP 连接线程 ID 不断变化,但是 @jtahlborn 注意到服务器端方法是同步的,因此它会串行执行,但不一定在单个线程中执行。

于 2013-02-12T19:15:04.117 回答
1

您的服务器端processInput()方法是同步的,所以,是的,调用将被串行处理。这与RMI有什么关系?

更新:

如果您想为每个客户端会话currentState设置单独的和currentJourney值,那么您需要使用 RMI 远程会话模式,请参阅此答案了解详细信息。

于 2013-02-12T19:11:50.983 回答