Java Servlet中的竞争条件

时间:2012-12-04 20:47:48

标签: java-ee servlets race-condition

我有这个奇怪的问题,两个单独的用户将图片上传到我的网络服务,而user1看到了user2的错误消息。查看代码对我来说没什么好处,所以我想问这个代码有什么问题,为什么会创建这样的条件,user1的错误会对user1可见?

这是一些尝试和演示这种情况的简化代码。

public class SomeService {

    private static SomeService service;
    private SomeService() {
    }

    public static SomeService getInstance() {
        if(service == null) {
           service = new SomeService();
        }
    }

    public ErrorStatus doSomething(ErrorStatus es) {

        es = new ErrorStatus(es);
        // stuff happens that causes an error
        es.addMessage(new ErrorMessage("some error happened"));
        return es;
    }

    public ErrorStatus doSomethingElse(ErrorStatus es) {

        es = new ErrorStatus(es);
        // stuff happens that causes an error
        es.addMessage(new ErrorMessage("some different error happened"));
        return es;
    }
}

public class ErrorMessage {
    String message;

    //simple constructor, getters and setters, nothing interesting
}

public class ErrorStatus {
    int id;
    String status;
    List<ErrorMessage> messages;

    public ErrorStatus() {
         id = 0;
         status = "";
         messages = new ArrayList<>();
    }

    public ErrorStatus(ErrorStatus other) {
        id = other.getId();
        status = other.getStatus();
        messages = other.getMessages();
    }

    public void addMessage(ErrorMessage message) {

        //data checks
        messages.add(message);
    }

    //getters and setters
}   

public class UploadServlet extends HttpServlet {

    public doGet(request, response) {

        ErrorStatus es = new ErrorStatus();
        SomeService service = SomeService.getInstance();

        es = service.doSomething(es);

        es = service.doSomethingElse(es);

        printErrors(response.getWriter(), es);

    }

    public void printErrors(PrintWriter pw, ErrorStatus es) {

        for(int i = 0; i < es.getMessages().size(); i++) {

            pw.write(es.getMessages().get(i).getMessage());
        }
    }
}

代码中的两个地方我认为可能发生的奇怪事情是复制构造函数或服务是单例的事实。也许我没有正确地复制列表,或者作为单例的服务可能会改变堆栈和堆的使用方式,我真的不确定。根据我对堆栈,堆,单例和servlet如何工作的理解,一个用户数据永远不会受到另一个用户数据的影响。 此外,他们只有部分问题是列表,原始数据总是正确的,只有错误列表才会出现错误的用户。

我应该注意,我已经解决了这个问题,我只是不明白为什么这是一个问题。解决方案是停止使用复制构造函数,并在doSomething和doSomethingElse方法中修改ErrorStatus对象。因此修改后的代码将具有doSomething的void返回类型,并且只调用es.addMessage,同样从ErrorStatus中删除了复制构造函数。

任何有助于理解为什么会导致比赛状况的帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

我认为你的问题在于这一行:

messages = other.getMessages();

基本上这意味着两个不同的ErrorStatus个对象共享相同的List,对一个列表的修改会影响另一个,反之亦然。这一行应该看起来像

messages = new ArrayList<ErrorMessage>(other.getMessages());

这样,列表将(最初)包含相同的元素,但不会受到修改副作用的影响。

答案 1 :(得分:0)

我同意其中一个回复,很难从简化代码中找出问题所在。我注意到一些错别字/错误:

public static SomeService getInstance() {
    if(service == null) {
       service = new SomeService();
    }
    **return service;**
}

public doGet(request, response) {

    ErrorStatus es = new ErrorStatus();
    SomeService service = SomeService.getInstance();

    es = service.doSomething(es); //<-- This first one never gets tracked; es is overwritten below.

    es = service.doSomethingElse(es); 

    printErrors(response.getWriter(), es);

}

public ErrorStatus() {
     id = 0; **<-- How exactly does id increase?**
     status = ""; 
     messages = new ArrayList<>();
}

在任何情况下,检查SomeService或UploadServlet中是否有成员变量。我自己正在学习种族条件;我怀疑你有两个用户访问共享资源的问题,并且访问的交错/时间是问题。

举个简单的例子,用户可能会: 获取servlet实例(gsi) - &gt;做行动(da) - &gt;记录错误(le) - &gt;读错误(重新)。

两个用户可以交错操作。

User1:gsi - &gt; da - &gt; le - &gt;重

User2: __ gsi - &gt; da - &gt; le - &gt;重。

因此,当user1读取错误时,他实际上正在读取user2的错误,user2刚刚完成了错误的记录。您的UploadServlet可能有一个成员ErrorStatus对象:这会产生问题。

祝你好运!

这是一个很好的教程:http://www.informit.com/articles/article.aspx?p=23947