简体   繁体   中英

Java Servlet: How to instantiate thread and receive messages

I already read the great article about Servlet Thread handling .

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.

I use JQuery AJAX call on browser-site an handled requests with timeouts.

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:

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

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. You should use Thread.sleep() instead. 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. You should thus either use a concurrent collection (like a ConcurrentLinkedQueue), or synchronize every access to the linked list. I would use a Concurrent collection.

And finally, several servlet threads read and modify the rmpThread and rmProducer variables in parallel, without any kind of synchronization. The rmpThread variable is written, but never read, so I would make it a local variable instead. 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).

So the rmProducer should be declared like this:

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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