高并发发生时的Servlet问题

时间:2013-05-15 08:34:52

标签: java concordion

这是与我之前的帖子相关的姐妹问题:     Simple web server when high-concurrency is met

面试问题是:

public class CounterServlet extends HttpServlet{

 private volatile static int counter=0;

 public int getCounter()
 {
   return counter;
 }

 public void service(HttpServletRequest request
                     HttpServletResponse response) throws IOException
 {
    counter++;
    PrintWriter out=response.getWriter();
    out.write("hello");
 }

遇到高并发时,上面的代码会出现什么问题? 我的分析是:Servlet是单例,因此同步中会出现问题。它已经将计数器声明为volatile,这不会阻止问题。我建议同步服务方法?

3 个答案:

答案 0 :(得分:2)

如果通过多个线程访问静态值,每个线程都可以拥有它的本地缓存副本!为避免这种情况,您可以将变量声明为static volatile,这将强制线程在每次全局值时读取。但是,volatile不能代替正确的同步!

您需要同步代码,尽管您只是在计数器上进行增量,但这并不意味着整个方法都是原子的。可能有多个线程使用来自寄存器的当前值同时递增它。这可能会导致不良后果。

您需要synchronize方法或使用AtomicInteger进行此类微不足道的操作。

答案 1 :(得分:1)

因为volatile只是告诉编译器不要优化这个变量,所以它不会帮助解决与并发相关的问题。

我不知道你要对counter做什么,因为你只是递增它但我们可以肯定,在N次调用service方法之后,计数器将不会等于N.

为了防止它,可能会使方法同步(我假设这不是正确的方法),在某个锁定对象上同步递增部分或(我认为这是最合适的方法)使用AtomicInteger而不是int - AtomicInteger类确保对象上的所有操作都以原子方式完成。

答案 2 :(得分:1)

你应该使用AtomicInteger来实现这个目的。但是因为只存在一个Servlet实例,它被重用于来自多个clients的多个请求。因此不要在Servlet中声明任何实例或类变量t使方法同步。如果你想使用原始int,你可以做类似下面的事情。

public void service(HttpServletRequest request
                 HttpServletResponse response) throws IOException
{
   synchronized (CounterServlet.class) {
        count++;
    }      
   PrintWriter out=response.getWriter();
   out.write("hello");
}