我有一个奇怪的问题,两个不同的用户将图片上传到我的网络服务,而 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 中删除。
任何帮助理解为什么这会导致竞争条件将不胜感激。