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 が変化し続けることがわかります。

于 2013-02-12T19:15:04.117 に答える
1

サーバー側のprocessInput()メソッドは同期されているため、はい、呼び出しはシリアルに処理されます。それはRMIと何の関係がありますか?

アップデート:

クライアントセッションごとに個別currentStatecurrentJourney値を設定する場合は、RMIリモートセッションパターンを使用する必要があります。詳細については、この回答を参照してください。

于 2013-02-12T19:11:50.983 に答える