简体   繁体   English

Java Servlet:如何实例化线程和接收消息

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

I already read the great article about Servlet Thread handling . 我已经阅读了关于Servlet线程处理的好文章。

I have the following problem: I want to create a simple Servlet that starts either a new Thread that produces in a first version random messages or - depending on the parameter - send a response with all messages that were produced since last request. 我有以下问题:我想创建一个简单的Servlet,它启动一个新的Thread,它在第一个版本中生成随机消息,或者 - 根据参数 - 发送一个响应,包含自上次请求以来生成的所有消息。

I use JQuery AJAX call on browser-site an handled requests with timeouts. 我在浏览器站点上使用JQuery AJAX调用处理请求超时。

When I run my receiver calls, I get only the first messages that were produced since the thread crashed in the meanwhile. 当我运行我的接收器调用时,我只获得自同时线程崩溃后产生的第一条消息。 It looks as it would be a thread-safety problem, described in the above-mentioned article, but I can figure it out exactly. 它看起来像是一个线程安全问题,在上面提到的文章中描述,但我可以准确地解决它。 The log gives me the following information: 该日志提供了以下信息:

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)

This is my current servlet code: 这是我当前的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();
}
}

And this is my simple message-producer thread: 这是我简单的消息生产者线程:

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

The requests are performed all 1000ms 请求全部执行1000毫秒

Do you probably see my error in reasoning? 你可能在推理中看到了我的错误吗?

Thanks a lot! 非常感谢!

You have serious thread-safety issues here. 你这里有严重的线程安全问题。

First of all, you're using wait() when all you want to do is sleep for a few milliseconds. 首先,当你想要做的就是睡几毫秒时,你正在使用wait() You should use Thread.sleep() instead. 您应该使用Thread.sleep()代替。 That will solve your exception, but won't solve the thread-safety issues. 这将解决您的异常,但不会解决线程安全问题。

You have a shared linked list that is used by multiple threads in parallel : the random generator thread stors messages in the queue whild the servlet thread(s) emoved messages from the queue. 您有一个并行多个线程使用的共享链接列表:随机生成器线程在队列中存储消息,而servlet线程则从队列中移除消息。 You should thus either use a concurrent collection (like a ConcurrentLinkedQueue), or synchronize every access to the linked list. 因此,您应该使用并发集合(如ConcurrentLinkedQueue),或者同步对链接列表的每次访问。 I would use a Concurrent collection. 我会使用Concurrent集合。

And finally, several servlet threads read and modify the rmpThread and rmProducer variables in parallel, without any kind of synchronization. 最后,几个servlet线程并行读取和修改rmpThreadrmProducer变量,没有任何同步。 The rmpThread variable is written, but never read, so I would make it a local variable instead. rmpThread变量是写的,但从不读,所以我会把它变成局部变量。 To make sure the newly written rmProducer is visible by the other servlets, you need to synchronize all its accesses, either by using a synchronized block to write and read it, or by making it volatile , or by wrapping it into an AtomicReferece (that's the choice I would make). 为了确保其他servlet可以看到新编写的rmProducer ,你需要同步它的所有访问,可以通过使用synchronized块来写入和读取它,也可以使它变为volatile ,或者将它包装到AtomicReferece中(这就是选择我会做的)。

So the rmProducer should be declared like this: 所以rmProducer应该像这样声明:

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

To modify its value, you should use 要修改其值,您应该使用

rmProducerRef.set(rmProducer);

And to get it, you shuld use 为了得到它,你可以使用它

rmProducerRef.get();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM