2013-02-12 125 views
1

我剛剛開始使用RMI,我試圖編寫一個模擬火車預訂系統的簡單程序。我已經設置了基本功能 - 導出了服務器,客戶端和遠程對象。一個客戶端連接可以正常工作。但是,當超過1個客戶端連接時,客戶端似乎在同一個線程中執行。當我在同一臺計算機上運行多個客戶端或從另一臺筆記本電腦連接客戶端時,就是這種情況。Java RMI服務器端線程

我的印象是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(字符串輸入)使用的私有方法)

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


} 
+0

這裏沒有證據表明服務器正在使用相同的線程。 – EJP 2013-02-12 21:54:44

回答

1

你的服務器端processInput()方法是同步的,所以,是的,通話將被處理seriall年。這與RMI有什麼關係?

UPDATE:

如果你想擁有獨立的 currentStatecurrentJourney值每個客戶端會話

,那麼你需要使用RMI遠程會話模式,見this answer瞭解詳情。

+0

正如我所提到的,客戶似乎在同一個線程中執行,無論是否同步processInput()。例如,我在客戶端連接上選擇了一些選項,我知道下一個服務器響應應該是什麼。然後,我連接另一個客戶端,希望看到最初的服務器響應,但我實際得到的是第一個客戶端連接的預期服務器響應。這表明只有一個BookingProcess線程可以爲客戶提供便利嗎?我認爲RMI會爲每個客戶端創建一個新線程?我誤解了嗎? – user1061799 2013-02-12 19:20:14

+0

這就是我關於RMI的問題...... – user1061799 2013-02-12 19:21:32

+0

@ user1061799 - 你意識到你在服務器上使用共享狀態,對吧? 'currentState'和'currentJourney'成員變量在_all_客戶端連接中共享。這與線程無關。 – jtahlborn 2013-02-12 19:22:41

4

至於RMI服務器線程,答案是它可能會或可能不會在單獨的線程中運行。看到這裏的文檔:在遠程方法調用

由RMI運行時調度到遠程對象實現可能會或可能不會在一個單獨的線程中執行的方法

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

3.2線程使用。 RMI運行時不保證將遠程對象調用映射到線程。由於同一遠程對象上的遠程方法調用可能同時執行,因此遠程對象實現需要確保其實現是線程安全的。

爲@jtahlborn注意到服務器端的方法是同步的,因此將串行執行,不一定在單個線程你可以把服務器端線程轉儲,你會看到,RMI TCP連接線程的ID是不斷變化的,但是雖然。

+0

感謝您花時間回答。非常感激。 – user1061799 2013-02-12 19:33:55

+0

這個規範的主要含義是*你不能認爲它是單線程的。* – EJP 2013-02-12 20:32:39

+0

非常感謝EJP。 – user1061799 2013-02-12 21:43:28