代码之家  ›  专栏  ›  技术社区  ›  John Rumpel

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

  •  0
  • John Rumpel  · 技术社区  · 13 年前

    我已经读过关于 Servlet Thread handling .

    我有以下问题:我想创建一个简单的Servlet,它启动一个新的线程,在第一个版本中产生随机消息,或者根据参数发送一个响应,其中包含自上次请求以来产生的所有消息。

    我在浏览器网站上使用JQueryAJAX调用,处理超时的请求。

    当我运行接收器调用时,我只得到自线程崩溃以来产生的第一条消息。这看起来像是一个线程安全问题,正如上面的文章所描述的,但我可以准确地解决它。 日志提供了以下信息:

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

    所有1000ms都执行请求

    你可能看到我推理的错误了吗?

    谢谢!

    1 回复  |  直到 9 年前
        1
  •  2
  •   JB Nizet    13 年前

    这里有严重的线程安全问题。

    首先,您正在使用 wait() 当你只想睡几毫秒的时候。你应该使用 Thread.sleep() 相反这将解决您的异常,但不会解决线程安全问题。

    您有一个由多个线程并行使用的共享链表:随机生成器线程将消息存储在队列中,而servlet线程从队列中删除消息。因此,您应该使用并发集合(如ConcurrentLinkedQueue),或者同步对链表的每次访问。我会使用Concurrent集合。

    最后,几个servlet线程读取并修改 rmpThread 和 rmProducer 并行变量,没有任何类型的同步。rmpThread变量是写的,但从来没有读过,所以我会把它变成一个局部变量。确保新编写的 RM生产者 如果其他servlet可见,则需要同步它的所有访问,要么使用同步块来写入和读取它,要么使它 volatile ,或者将其包装到AtomicReferece中(这是我会做出的选择)。

    因此,rmProducer应该这样声明:

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

    要修改其值,应使用

    rmProducerRef.set(rmProducer);
    

    为了得到它,你必须使用

    rmProducerRef.get();
    
    推荐文章