0

我有一个奇怪的问题,两个不同的用户将图片上传到我的网络服务,而 user1 看到了 user2 的错误消息。查看代码对我来说没有什么特别之处,所以我想问一下这段代码有什么问题,为什么它会造成这种情况,user2 的错误对 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 如何工作的理解,一个用户的数据永远不会受到另一个用户数据的影响。此外,他们唯一有问题的部分是列表,原始数据始终是正确的,只有错误列表会显示给错误的用户。

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

任何帮助理解为什么这会导致竞争条件将不胜感激。

4

2 回答 2

1

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

messages = other.getMessages();

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

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

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

于 2012-12-04T22:28:43.440 回答
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) --> 执行操作 (da) --> 记录错误 (le) --> 读取错误 (re)。

两个用户可以交错操作。

用户 1:gsi --> da --> le --> re

用户 2: __ gsi --> da --> le --> re.

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

祝你好运!

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

于 2013-04-25T23:57:02.523 回答