簡體   English   中英

Java Servlet:如何實例化線程和接收消息

[英]Java Servlet: How to instantiate thread and receive messages

我已經閱讀了關於Servlet線程處理的好文章。

我有以下問題:我想創建一個簡單的Servlet,它啟動一個新的Thread,它在第一個版本中生成隨機消息,或者 - 根據參數 - 發送一個響應,包含自上次請求以來生成的所有消息。

我在瀏覽器站點上使用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)

這是我當前的servlet代碼:

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

請求全部執行1000毫秒

你可能在推理中看到了我的錯誤嗎?

非常感謝!

你這里有嚴重的線程安全問題。

首先,當你想要做的就是睡幾毫秒時,你正在使用wait() 您應該使用Thread.sleep()代替。 這將解決您的異常,但不會解決線程安全問題。

您有一個並行多個線程使用的共享鏈接列表:隨機生成器線程在隊列中存儲消息,而servlet線程則從隊列中移除消息。 因此,您應該使用並發集合(如ConcurrentLinkedQueue),或者同步對鏈接列表的每次訪問。 我會使用Concurrent集合。

最后,幾個servlet線程並行讀取和修改rmpThreadrmProducer變量,沒有任何同步。 rmpThread變量是寫的,但從不讀,所以我會把它變成局部變量。 為了確保其他servlet可以看到新編寫的rmProducer ,你需要同步它的所有訪問,可以通過使用synchronized塊來寫入和讀取它,也可以使它變為volatile ,或者將它包裝到AtomicReferece中(這就是選擇我會做的)。

所以rmProducer應該像這樣聲明:

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

要修改其值,您應該使用

rmProducerRef.set(rmProducer);

為了得到它,你可以使用它

rmProducerRef.get();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM