这段代码有什么缺陷吗?

时间:2011-05-31 04:16:21

标签: java servlets concurrency

我编写了一个servlet,但是servlet尚未处于生产阶段。

我在servlet的Filter中添加了一个计数器,这样当并发请求数达到限制时,就不能再接受任何人了。我担心一些边缘情况,例如:假设系统已经达到49个并发请求(50个是最大值),并且在synchronized块中它使布尔变量“ok”为True,然后在下一个实例中,多个线程看到servlet是可用的,赶紧进入并打破限制。

如果有任何缺陷,请帮助检查此代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        // place your code here

        // pass the request along the filter chain
        conditionalInfoLog(logEnabled, "Incoming request...");
        conditionalInfoLog(logEnabled, "Number of concurrent request(s): " + count);
        boolean ok;
        synchronized (lock) {
            ok = count < limit;
            if(ok){
                count++;
            }
        }
        if (ok) {
            try{
                // let the request through and process as usual
                conditionalInfoLog(logEnabled, "Request accepted and processing, number of concurrent request(s): " + count);
                chain.doFilter(request, response);

            }catch(Exception ex){
                conditionalErrorLog(logEnabled, ex.getMessage());
                String xmlStr = genXmlErrMsg(ex.getMessage());
                response.setContentType("text/xml");
                response.getWriter().print(xmlStr);
            }finally{
                synchronized (lock) {
                    count--;
                }
                conditionalInfoLog(logEnabled, "End of request. Number of concurrent request(s): " + count);
            }
        } else {
            // handle limit case, e.g. return status code 503
            conditionalInfoLog(logEnabled, busyMsg);
            String xmlStr = genXmlErrMsg(busyMsg);
            response.setContentType("text/xml");
            response.getWriter().print(xmlStr);
        }

    }

3 个答案:

答案 0 :(得分:5)

我宁愿在servletcontainer级别配置它而不是试图拼凑一些东西。一个不错的servletcontainer经过全面测试,确保生产就绪。

例如,Tomcat完全具有maxThreads属性用于此目的。您可以在<Connector>中的server.xml元素中进行设置。

<Connector maxThreads="50" ...>

这限制了同时请求的数量(顺便说一下,默认为200)。因此,当有第51个请求时,它只是放入一个队列(其长度可由acceptCounts属性配置),直到第一个准备好。这也比503更加用户友好。

另见:

答案 1 :(得分:3)

这是正确的,但你基本上是在重新发明java.util.concurrent.Semaphore

class MyFilter {
  final Semaphore permits = new Semaphore(50);

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (permits.tryAcquire()) {
        try {
          … // execute
        } finally {
          permits.release();
        }
    } else {
      … // handle limit case, e.g. return status code 503
    }
  }
}

此课程比您的手动解决方案更有效,无论如何都能更好地表达您的意图。

答案 2 :(得分:0)

一次最多允许一个线程同步一个对象。如果有几个线程试图“冲进”同步块,那么只有一个线程可以实现。此外,每个线程都有ok的本地副本,每个线程都必须通过此块:

synchronized (lock) {
    ok = count < limit;
    if (ok) {
        count++;
    }
}

之前它可以测试它的ok变量的值。所以我觉得这很安全。

稍后同步count的减量也是很好的;这将确保在任何其他线程可以进入在count上同步的块之前,lock的值将被刷新到内存。