0

2 人の別々のユーザーが私の Web サービスに画像をアップロードし、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());
        }
    }
}

コード内で奇妙なことが起こっている可能性があると思われる 2 つの場所は、コピー コンストラクター、またはサービスがシングルトンであるという事実です。リストを正しくコピーしていないか、シングルトンであるサービスがスタックとヒープの使用方法を変更している可能性がありますが、よくわかりません。スタック、ヒープ、シングルトン、およびサーブレットがどのように機能するかについての私の理解から、あるユーザーのデータが別のユーザーのデータの影響を受けることは決してありません。また、これまでに問題があったのはリストの一部だけでした。プリミティブ データは常に正しく、エラーのリストだけが間違ったユーザーに表示されていました。

私は問題を解決しましたが、なぜそれが問題なのか理解できませんでした。解決策は、コピー コンストラクターの使用を停止し、ErrorStatus オブジェクトが doSomething および doSomethingElse メソッドで変更されるようにすることでした。そのため、改訂されたコードでは、doSomething の戻り値の型が void になり、es.addMessage を呼び出すだけになり、コピー コンストラクターも ErrorStatus から削除されました。

これが競合状態を引き起こした理由を理解する助けがあれば幸いです。

4

2 に答える 2

1

あなたの問題は次の行にあると思います:

messages = other.getMessages();

基本的に、これは 2 つの異なるErrorStatusオブジェクトが同じものを共有していることを意味しList、一方のリストを変更すると他方に影響し、その逆も同様です。この行は次のようになります

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

このようにして、リストには(最初は)同じ要素が含まれますが、変更の副作用の影響を受けません。

于 2012-12-04T22:28:43.440 に答える
0

単純化されたコードから問題を把握するのは難しいという回答の1つに同意します。私が気づいたいくつかのタイプミス/エラー:

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 にメンバー変数があるかどうかを確認してください。私は自分で競合状態について学んでいます。2 人のユーザーが共有リソースにアクセスしているという問題があり、アクセスのインターリーブ/タイミングが問題であると思われます。

簡単な例として、ユーザーは、サーブレット インスタンスを取得する (gsi) --> アクションを実行する (da) --> エラーをログに記録する (le) --> エラーを読み取る (re) とします。

2 人のユーザーが操作をインターリーブする場合があります。

User1: gsi --> da --> le --> re

User2: __ gsi --> da --> le --> re.

したがって、user1 がエラーを読み取るとき、実際には、エラーのログ記録を完了したばかりの user2 によってエラーが読み取られます。UploadServlet にメンバー ErrorStatus オブジェクトが含まれている可能性があります。これにより、問題が発生します。

幸運を!

ここに良いチュートリアルがあります: http://www.informit.com/articles/article.aspx?p=23947

于 2013-04-25T23:57:02.523 に答える