2012-04-01 7 views
1

저는 클라이언트 (클라이언트 당 하나의 스레드)로부터 다양한 xml 메시지를 받고 메시지 유형에 따라 다른 기능으로 메시지를 라우팅하는 서버를 가지고 있습니다. 예 : 메시지의 첫 번째 요소에 'login'문자열이 포함되어 있으면이 메시지가 로그인 메시지임을 나타내며 메시지를 login() 함수로 라우팅합니다.어떻게이 스레드를 안전하게 만들 수 있습니까?

어쨌든 여러 클라이언트가 연결되어 있고 디스패처가 메시지 라우팅 중간에 스레드를 전환하면 상황이 엉망이되지 않도록이 메시지를 작성하려고합니다.

public void processMessagesFromClient(Client client) 
{ 
    Document message; 

    while (true) 
    { 
     try 
     { 
      message = client.inputStream.readObject(); 

      /* 
      * Determine the message type 
      */ 
      String messageType = getMessageType(message); 

      // Route the message depending on its type 
      switch (messageType) 
      { 
       case LOGIN: 
        userModel.handleLogin(); 
       ... 
       ... 
       ... 
       etc... 
      } 
     } catch(Exception e) {} 
    } 

그래서 어떻게이 스레드로부터 안전 할 수 있습니다 - 그래서 여기에 어떻게 메시지를 라우팅하고있다? 내가 어딘가에 있지만 어디 있는지 모르겠 동기화 성명을 넣어야 할 그림. (내 https://stackoverflow.com/a/416198/1088617

그리고 싱글이에 동기화 사용에 적합하지 않습니다 말한다 여기에 또 다른 포스트 - 또한 필자는 주제에 주위를 읽고 난 '이'동기 사용에 문제가 말한다이 게시물을 발견 위의 코드에서 클래스는 싱글 톤입니다.) - https://stackoverflow.com/a/416202/1088617

+0

로 설정되어 있는지 확인해야합니까? – GavinCattell

+0

위의 기능은 모든 클라이언트에서 사용됩니다. 나는 각 클라이언트에 대한 스레드를 시작하고 먼저 input/outputstreams를 검색 한 후 위의 processMessagesFromClient() 메소드를 반복적으로 반복합니다. 따라서 모든 클라이언트는 '메시지'변수를 공유하게됩니다. 그들은 또한 싱글 톤이기 때문에 모두 userModel을 공유합니다. 클라이언트로부터 메시지를 받으면 그 메시지를 처리 ​​할 때까지 다른 스레드를 차단해야한다고 생각하니? –

답변

2

로컬 변수 만 사용 중이므로 클래스가 이미 스레드 안전성입니다. 스레드 안전은 코드 상태 인 (즉, 필드)에 액세스 할 때만 발생하며 코드에서는 수행하지 않습니다.

무슨 소리하는 직렬화 - 당신은 그 메시지 처리를 보장하기 위해 하나 개의 지점을 통해 모든 메시지 처리를 깔때기 할 것은 한 번에 하나씩 (시작하고 원자 완료)입니다. 이 솔루션은 간단

public void processMessagesFromClient(Client client) { Document Message; while (true) { processMessage(client); } } private static synchronized processMessage(Client client) { try { message = client.inputStream.readObject(); String messageType = getMessageType(message); // Route the message depending on its type switch (messageType) { case LOGIN: userModel.handleLogin(); ... etc... } } catch(Exception e) {} } 

가 FYI static synchronized 방법 로크로 클래스 오브젝트를 사용 : static synchronized 방법을 사용한다.이 코드는 코드가 단일 스레드처럼 동작하도록하여 질문이 원하는 것처럼 보입니다.

+0

건배 메이트, 로컬 변수가 스레드로부터 안전하다는 사실을 잊어 버렸습니다. –

+2

이 코드를 사용할 때주의해야합니다. "message = client.inputStream.readObject()"에서 무엇을 기대합니까? 호출을 차단하고있을 수 있습니다. 현재 클린이 메시지를 보낼 때까지 대기합니다. 이 경우 서버가 특정 클라이언트의 메시지를 기다리고 있으면 모든 클라이언트의 처리가 중지됩니다. Brian_CS : "readObject()"가 메시지를 기다리는 경우 명확히 할 수 있습니까? –

+0

예, 메시지를 기다립니다. inputStream은 ObjectInputStream입니다. –

0

어떤 리소스가 특정 시간에 하나의 스레드로만 사용해야 하는지를 알아야합니다.

다음 메시지를 읽는 것이 보호되어야하는 경우가 있습니다.

synchronize (lock) { 
     message = client.inputStream.readObject(); 
} 

그러나, 코드 샘플은 정말 당신이 스레드 당 이러한 개체 중 하나가 제공 동시 액세스

+0

예 '메시지'를 보호해야한다고 생각합니다. 또한 userModel은 현재 클라이언트와 관련된 데이터를로드 할 때 보호되어야합니다. 현재 메시지를 처리 ​​할 때까지 실행중인 다른 모든 클라이언트 스레드를 차단하고 싶습니다. 그렇다면 동기화 블록에서 'try'문으로 모든 것을 래핑하는 것이 좋을까요? –

+0

@stefan bachert : 당신이 말했듯이 충분한 정보가 없습니다 ...하지만 inputStream이 네트워크 스트림이고 readObject()가 차단 될 수 있다고 가정하면이 호출을 동기화 된 블록에 넣는 것은 좋지 않습니다. 스레드 중 하나가 "readObject()"를 차단하면 다른 모든 스레드는 이미 사용 가능한 메시지를 읽을 수 없습니다. 이것이 문제가 될 수 있다고 생각하십니까? –

+0

이미 동기화 된 항목을 동기화하면 데드락이 발생할 수 있습니다. 너의 의도를 알 겠어. –

1

보호에 필요한 사항이 표시되지 않습니다, 당신은 문제가 없습니다. 스레드 중 하나에 의해 수정 될 수있는 공유 객체 만 동기화하면됩니다. 이미 연결 당 하나의 스레드가있는 경우

public void processMessagesFromClient(Client client) {  
    while (true) { 
     processMessage(client); 
    } 
} 

private void processMessage(Client client) { 
    try { 
     Document message = client.inputStream.readObject(); 

     String messageType = getMessageType(message); 

     // Route the message depending on its type 
     switch (messageType) { 
      case LOGIN: 
       userModel.handleLogin(); 
      ... 
      etc... 
     } 
    } catch(Exception e) {} 
} 
+0

하지만 그는 코드의 클래스가 싱글 톤이라고 말했다. 그래서 아마도 스레드간에 공유되는 데이터 구조를 가지고있을 것입니다. –

+0

만약 위의 코드가 실행 중이고 특정 클라이언트 (클라이언트 1)에 대해 'message'변수가 설정되면 ... 디스패처가 인터럽트하고 다른 클라이언트 (클라이언트 2)의 스레드를 실행할 수 없도록하면 '메시지 '변수가 새 클라이언트에 의해 덮어 쓰여질 수 있습니까? 다시 클라이언트 1로 전환하면 '메시지'변수에 클라이언트 2와 관련된 데이터가 포함될 수 있습니까? –

+0

ObjectInputStream을 인터럽트 할 수 없습니다. 제안한대로 연결 당 하나의 스레드를 사용했는데 이는 다중 연결에 공유 스레드를 사용하는 디스패처를 사용할 수 없음을 의미합니다. 당신이하는 일을 분명히 할 수 있습니까? –

1

, 당신은 동기화 할 수있는 유일한 방법은 이벤트 (userModel.handleLogin() 같은 즉, 기능)을 처리하는 기능은 다음과 같습니다.

1

가장 좋은 해결책은 ConcurrentQueue와 같은 스레드 안전 큐를 사용하고 단일 작업 스레드를 사용하여이 값을 선택하고 하나씩 작업을 실행하는 것입니다.

2

실제로 수신 메시지를 읽는 메시지 처리기 스레드가 있습니다. 그러면 처리기가 작업자 스레드로 넘겨 주어 시간이 오래 걸리는 메시지 처리가 수행됩니다. Java ThreadPoolExecutor를 사용하여이를 관리 할 수 ​​있습니다.

+0

건배, 아픈 그 모습. –

0

메서드 자체는 스레드로부터 안전합니다. 그러나이 클래스가 싱글 톤임을 알게되면 getInstancedouble checked locking을 사용하여 스레드 안전을 보장 할 수 있습니다. 또한 당신은 당신의 인스턴스가 새 스레드를 시작 static

 class Foo { 
      private static volatile Foo instance = null; 
      public static Foo getInstance() { 
       if (instance == null) 
       { 
        synchronized(this) 
        { 
         if (instance == null) 
          instance = new Foo(); 
        } 
       } 
       return instance ; 
      } 
     } 
관련 문제