2013-04-22 1 views
0

나는 이미 Servlet Thread handling에 대한 위대한 기사를 읽었습니다.자바 서블릿 : 스레드를 인스턴스화하고 메시지를받는 방법

다음과 같은 문제가 있습니다. 첫 번째 버전의 임의의 메시지를 생성하는 새 스레드 또는 매개 변수에 따라 마지막 요청 이후 생성 된 모든 메시지와 함께 응답을 보내는 간단한 서블릿을 만들고 싶습니다. .

브라우저 사이트에서 JQuery AJAX 호출을 사용하여 처리 된 요청에 시간 초과가 발생했습니다.

수신자 호출을 실행하면 그 사이에 스레드가 충돌 한 이후로 생성 된 첫 번째 메시지 만 표시됩니다. 그것은 위에서 언급 한 기사에서 설명한 스레드 안전 문제가 될 것으로 보이지만 정확히 파악할 수 있습니다. 이건 내 현재 서블릿 코드

SEVERE: Exception in thread "Thread-75" 
SEVERE: java.lang.IllegalMonitorStateException 
    at java.lang.Object.wait(Native Method) 
    at com.lancom.lsr.util.RandomMessageProducer.run(RandomMessageProducer.java:35) 
    at java.lang.Thread.run(Thread.java:722) 

SEVERE:  at java.lang.Object.wait(Native Method) 
SEVERE:  at com.lancom.lsr.util.RandomMessageProducer.run(RandomMessageProducer.java:35) 
SEVERE:  at java.lang.Thread.run(Thread.java:722) 

입니다

: 로그는 나에게 다음과 같은 정보를 제공

public class MyServlet extends HttpServlet { 
... 
private RandomMessageProducer rmProducer; 
private Thread rmpThread; 
... 

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
    Map<Integer, String> msg = new HashMap<Integer, String>(); 
    Gson gson = new Gson(); 

    String sDevs = request.getParameter("devices"); // Option 1 
    String sGetMsg = request.getParameter("rec_msg"); // Option 2 
    PrintWriter pw = response.getWriter(); 

    // Request: Send information and run thread 
    if (sDevs != null && STATUS==0) {      

     /* Start a dummy producer */    
     rmProducer = new RandomMessageProducer(); 
     rmpThread = new Thread(rmProducer) 
     rmpThread.start(); 

     pw.print("{\"1\": \"Action started!\"}"); 
     STATUS=1; 
    } 
     // Request: Receive messages 
    else if (sGetMsg != null) { 
     List<String> logs = rmProducer.getLogMsg();  
     for (String lmsg : logs) { 
      // Check if we can update the current Status 
      if (msg.equals("<<<FIN_SUCC>>>") || msg.equals("<<<FIN_ERR>>>")) { 
       STATUS=0;      
      } 
      msg.put(0, lmsg); 
     } 
     String json = gson.toJson(msg);  
     pw.print(json); 
    } 
    pw.close(); 
} 
} 

을 그리고 이것은 내 간단한 메시지 생산자 스레드 :

public class RandomMessageProducer implements Runnable { 

    private Queue<String> msgQueue = new LinkedList<String>(); 

    @Override 
    public void run() { 
     Random randomGenerator = new Random(); 
     for (int idx = 1; idx <= 100; ++idx){ 
      int randomInt = randomGenerator.nextInt(100); 
      msgQueue.add("Generated : " + randomInt); 
      try { 
      wait(500);   
      } catch (InterruptedException e) { 
      msgQueue.add("<<<FIN_ERR>>>"); 
      e.printStackTrace(); 
      } 
     } 
     msgQueue.add("<<<FIN_SUCC>>>"); 
    } 

    public List<String> getLogMsg() { 
     List<String> res = new ArrayList<String>(); 
     while (!msgQueue.isEmpty()) { 
      res.add(msgQueue.poll()); 
     } 
     return res; 
    } 
} 

요청은 모두 수행 1000ms

당신은 아마 추론에서 내 오류를 볼 수 있습니까?

고맙습니다.

+0

를 사용 shuld 값을 수정하려면 어떻게 RandomMessageProducer의 35 번째 줄입니까? – Elior

답변

2

여기에 심각한 스레드 안전 문제가 있습니다.

우선, 몇 밀리 초 동안 자고 싶을 때 wait()을 사용하고 있습니다. 대신 Thread.sleep()을 사용해야합니다. 그렇게하면 예외가 해결되지만 스레드 안전 문제는 해결되지 않습니다.

여러 스레드가 병렬로 사용하는 공유 링크 목록이 있습니다. 임의 생성기 스레드는 서블릿 스레드가 큐에서 메시지를 가져온 큐의 메시지를 저장합니다. 따라서 Concurrent Collection (ConcurrentLinkedQueue와 같은)을 사용하거나 링크 된 목록에 대한 모든 액세스를 동기화해야합니다. 동시 수집을 사용합니다.

마지막으로 여러 서블릿 스레드가 어떤 종류의 동기화없이 병렬로 rmpThreadrmProducer 변수를 읽고 수정합니다. rmpThread 변수는 쓰여졌지만 결코 읽지는 않습니다. 그래서 대신 로컬 변수로 만들 것입니다. 새로 작성된 rmProducer을 다른 서블릿에서 볼 수있게하려면 동기화 된 블록을 사용하여 쓰기 및 읽기를 수행하거나 volatile으로 만들거나, AtomicReferece로 래핑하여 모든 액세스를 동기화해야합니다. 내가 할 수있는 선택).

은 그래서 rmProducer은 다음과 같이 선언한다 :

private AtomicReference<RandomMessageProducer> rmProducerRef = new AtomicReference<>(); 

, 당신은

rmProducerRef.set(rmProducer); 

를 사용해야합니다 그리고 그걸 얻기 위해, 당신은

rmProducerRef.get(); 
+0

음, 유효한 스레드 처리에 신경 써야합니다.자세한 설명 주셔서 감사합니다! –

관련 문제