2012-01-03 4 views
1

이 방법은 메시지 처리를 시작하는 이벤트 루프를 알리지 않습니다. 그러나 이벤트 루프가 이미 메시지를 처리 ​​중이면이 메서드는 이벤트 루프가 끝날 때 트리거되는 완료된 이벤트 처리 알림을받을 때까지 차단합니다.서버 모드의 스레드 차단

public void processEvent(EventMessage request) throws Exception { 
    System.out.println("processEvent"); 

    if (processingEvent) { 
     synchronized (eventCompleted) { 
      System.out.println("processEvent: Wait for Event to completed"); 
      eventCompleted.wait(); 
      System.out.println("processEvent: Event completed"); 
     } 
    } 

    myRequest = request; 
    processingEvent = true; 
    synchronized (eventReady) { 
     eventReady.notifyAll(); 
    } 
} 

이것은 클라이언트 모드에서 작동합니다. 서버 모드로 전환하고 이벤트 루프 처리에 소요 된 시간이 너무 빠르면 위의 방법으로 이벤트가 완료 될 때까지 기다릴 수 없습니다. 어떤 이유로 이벤트 complete 통지는 processingEvent 검사 이후와 eventCompleted.wait() 전에 전송됩니다. 출력 문을 제거해도 아무런 차이가 없습니다. 나는 클라이언트 모드에서 같은 문제를 반복 할 수 없다.

왜 이것은 서버 모드에서만 발생하며 이런 일이 발생하지 않도록하려면 어떻게해야합니까? - BTW 약 300 이벤트 후 randomely 클라이언트 모드에서 일어난

public void run() { 
    try { 
     while (true) { 
      try { 
       synchronized (eventReady) { 
        eventReady.wait(); 
       } 
       nx.processEvent(myRequest, myResultSet); 
       if (processingEvent > 0) { 
        notifyInterface.notifyEventComplete(myRequest); 
       } 
      } catch (InterruptedException e) { 
       throw e; 
      } catch (Exception e) { 
       notifyInterface.notifyException(e, myRequest); 
      } finally { 
       processingEvent--; 
       synchronized (eventCompleted) { 
        eventCompleted.notifyAll(); 
       } 
      } 
     } // End of while loop 
    } catch (InterruptedException Ignore) { 
    } finally { 
     me = null; 
    } 

여기 교착 상태 문제없이 작동하는 것 같다 개정 코드 :

다음은 eventReady 기다려 eventCompleted 알림입니다. processingEvent 만약

private BlockingQueue<EventMessage> queue = new SynchronousQueue<EventMessage>(); 

public void processEvent(EventMessage request) throws Exception { 
    System.out.println("processEvent"); 

    queue.put(request); 
} 


public void run() { 
    try { 
     while (true) { 
      EventMessage request = null; 
      try { 
       request = queue.take(); 
       processingEvent = true; 
       nx.processEvent(request, myResultSet); 
       notifyInterface.notifyEventComplete(request); 
      } catch (InterruptedException e) { 
       throw e; 
      } catch (Exception e) { 
       notifyInterface.notifyException(e, request); 
      } finally { 
       if (processingEvent) { 
        synchronized (eventCompleted) { 
         processingEvent = false; 
         eventCompleted.notifyAll(); 
        } 
       } 
      } 
     } // End of while loop 
    } catch (InterruptedException Ignore) { 
    } finally { 
     me = null; 
    } 
} 

답변

0

코드에 여러 경쟁 조건이 있습니다. processingEventvolatile을 신고하거나 AtomicBoolean을 사용해도 도움이되지 않습니다. 처리기가 준비 될 때까지 이벤트를 차단하는 SynchronousQueue을 사용하는 것이 좋습니다. 비슷해 :

private final BlockingQueue<Request> queue = new SynchronousQueue<Request>(); 
... 

// this will block until the processor dequeues it 
queue.put(request); 

그러면 이벤트 처리기는 않는다 :

while (!done) { 
    // this will block until an event is put-ed to the queue 
    Request request = queue.take(); 
    process the event ... 
} 

하나 개의 요청 등 한번에 모든 동기화 처리 될 것이다 SynchronousQueue 의해 처리 될 것이다.

+0

귀하의 의견을보기 전에 귀하의 방법을 구현했습니다. 그것은 지금 위대한 작품! – mtse

+0

나는 LinkedBlockingQueue가 0이 아닌 1로 제한된다는 것을 믿는다. API 문서에 따르면 0 제한은 생성자에서 IllegalArgumentException을 던질 것이다. –

+0

1 번으로 대기열 1 이벤트가됩니다. 1 작업 및 1 대기 중. 0은 이벤트 처리 프로그램에 의해 대기열에서 제거 될 때까지 제공자가 차단됨을 의미합니다. 대기중인 이벤트가 없습니다. 0이 전형적인 패턴입니다. – Gray

0

volatile를 선언 또는 다음 하나 개의 스레드에 의해 업데이트가 즉시 다른 스레드에 가시가되지 않을 수 있습니다 synchronized 블록 내에서 액세스 할 수 없습니다. 그러나 이것이 코드인지 여부는 명확하지 않습니다.

"서버"VM은 "클라이언트"VM을 사용할 때이 문제가 발생하지 않는 이유 일 수있는 속도 (시작 시간과 메모리 사용량을 희생 시키므로)가 최적화되어 있습니다.

1

notifyAll을 호출하고 wait() ing 스레드가없는 경우 알림은 손실됩니다.

올바른 방법은 notify()를 호출 할 때 synchronized 블록 내부의 상태를 항상 변경하고 wait()를 호출하기 전에 동기화 된 블록 내에서 항상 상태를 확인하는 것입니다.

processingEvent의 사용은 스레드로부터 안전하지 않습니다.

eventReady에서 대기하는 코드를 제공하고 eventCompleted에게 알릴 수 있습니까?

응용 프로그램의 속도를 높이거나 속도를 느리게하면 프로그램이 제대로 작동하지 않을 수 있습니다. -client을 사용하지만 다른 시스템, JVM 또는 JVM 옵션을 사용하는 경우 실패 할 수 있습니다.

+0

당신은 대기()를 호출하기 전에 상태를 확인 언급하지만 경쟁 조건을 방지하기 위해 동기화의 몇 가지 유형을 필요로하므로 문제가 항상 상태를 확인하고 대기를 호출 사이에 간격이있을 것입니다 (상태/상태 업데이트 방법에 관계없이). –

+0

@ increment1 감사합니다. 설명을 추가했습니다. –

+0

내 생각에 추가 된 동기화 블록이 교착 상태 문제를 일으킬 수 있다고 생각합니다. 동일한 잠금을 동기화하려는 경우 대기중인 스레드가 해당 잠금을 보유하고 있으면 해제 스레드가 잠금 및 트리거를 획득 할 수 없으므로 통지. –

0

서버 VM을 사용하여 분노 할 수있는 경쟁 조건이 있습니다. 처리 이벤트가 휘발성이 아닌 경우 서버 VM 또는 해당 환경에서 수행 한 특정 최적화가 문제에 영향을 미칠 수 있습니다.

코드에 문제가 있습니다 (이 방법은 여러 스레드가 동시에 액세스한다고 가정 함) processingEvent 및 eventCompleted.wait() 확인 사이에 다른 스레드가 이미 processingEvent를 false로 설정하고 알릴 수 있습니다.

차단 문제를 해결하는 가장 간단한 방법은 직접 관리하지 않고 JVM에서 공유 잠금을 사용하여 (한 번에 하나의 이벤트 만 처리하려는 경우) 처리하도록하는 것입니다. 예를 들어 전체 메서드를 동기화 할 수 있으므로 걱정할 필요가 없습니다.

두 번째 간단한 솔루션은 이벤트 전달을 위해 SynchronousQueue (이것은 설계된 상황 유형)를 사용하는 것입니다. 또는 실행중인 스레드가 더 많고 한 번에 대기열에 둘 이상의 요소가 필요한 경우 대신 ArrayBlockingQueue를 사용할 수 있습니다. 예 :

private SynchronousQueue<EventMessage> queue = new SynchronousQueue<EventMessage>(); 

public void addEvent(EventMessage request) throws Exception 
{ 
    System.out.println("Adding event"); 

    queue.put(request); 
} 

public void processNextEvent() 
{ 
    EventMessage request = queue.take(); 
    processMyEvent(request); 
} 

// Your queue executing thread 
public void run() 
{ 
    while(!terminated) 
    { 
    processNextEvent(); 
    } 
} 
+0

FIFO가 필요하고 processEvent 호출 스레드를 차단하는 시간을 제어 할 수 있기 때문에 LinkedBlockingQueue를 사용했습니다. – mtse

+0

@mtse SynchronousQueue는 크기가 없으며 본질적으로 하나의 요소입니다 (크기가 1 인 LinkedBlockingQueue와 동일한 기능을 갖지만, 더 나은 성능을 기대합니다). 모든 대기열에는 FIFO 기능이 있으며 모든 차단 대기열은 제안 및 폴링을 통해 제한된 시간 동안 차단할 수 있습니다. 그레이의 대답이 마음에 든다면 그것을 업 보셋 (위쪽 화살표)하고 받아 들여야합니다 (체크 표시). –

+0

나는 투표를하고 싶지만 충분히 높은 평판을 얻지는 못한다. 이것은 Stackoverflow에 대한 나의 첫 번째 질문이다! – mtse