这是与我之前的帖子相关的姐妹问题: 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,这不会阻止问题。我建议同步服务方法?
答案 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");
}