[英]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線程並行讀取和修改rmpThread
和rmProducer
變量,沒有任何同步。 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.