2013-02-12 140 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