6

クライアントリクエストのフローを処理したい。各リクエストには特別なタイプがあります。まず、そのタイプのデータを初期化する必要があります。その後、リクエストの処理を開始できます。クライアントタイプが初めて来るときは、対応するデータを初期化するだけです。この後、そのタイプの以下のすべてのリクエストは、そのデータを使用して処理されます。

これをスレッドセーフな方法で行う必要があります。

これが私が書いたコードです。スレッドセーフですか?

public class Test {

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>();

    /* to process client request we need to 
    create corresponding client type data.
    on the first signal we create that data, 
    on the second - we process the request*/

    void onClientRequestReceived(int clientTypeIndex) {
        if (clientTypesInitiated.put(clientTypeIndex, "") == null) {
            //new client type index arrived, this type was never processed
            //process data for that client type and put it into the map of types
            Object clientTypeData = createClientTypeData(clientTypeIndex);
            clientTypesInitiated.put(clientTypeIndex, clientTypeData);
        } else {
            //already existing index - we already have results and we can use them
            processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex));
        }
    }

    Object createClientTypeData(int clientIndex) {return new Object();}

    void processClientUsingClientTypeData(Object clientTypeData) {}
}

一方では、ConcurrentHashMapは同じAに対してmap.put(A、B)== nullを2回生成できません。他方では、割り当てと比較の操作はスレッドセーフではありません。

それで、このコードは大丈夫ですか?そうでない場合、どうすれば修正できますか?

更新:Martin Serranoのコードはスレッドセーフであり、初期化の二重の問題が発生しにくいため、私はMartinSerranoの回答を受け入れました。ただし、私のバージョンでは問題が見つからず、以下の回答として投稿されており、私のバージョンでは同期が必要ないことに注意してください。

4

8 に答える 8

3

いいえ、まだスレッドセーフではないと思います。

put操作を同期ブロックにラップする必要があります。

ConcurrentHashMapのjavadocによる

通常、取得操作(getを含む)はブロックされないため、更新操作(putおよびremoveを含む)と重複する場合があります。

于 2012-09-18T19:17:15.273 に答える
2

ここではputIfAbsentを使用する必要があります。この操作のセマンティクスは、CASに似ており、確かにアトミックです。そしてそれはアトミックなので、内部の割り当てや比較に問題はありません。

現在のコードはスレッドセーフではありません。

于 2012-09-18T19:20:40.513 に答える
2

このコードはスレッドセーフではありません。

//already existing index - we already have results and we can use them
processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex));

プットチェックに一時的に挿入した「」値を取得する可能性があります。

このようにして、このコードをスレッドセーフにすることができます。

public class Test {

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>();

    /* to process client request we need to 
       create corresponding client type data.
       on the first signal we create that data, 
       on the second - we process the request*/

void onClientRequestReceived(int clientTypeIndex) {
    Object clientTypeData = clientTypesInitiated.get(clientTypeIndex);
    if (clientTypeData == null) {
        synchronized (clientTypesInitiated) {
          clientTypeData = clientTypesInitiated.get(clientTypeIndex);
          if (clientTypeData == null) {
              //new client type index arrived, this type was never processed
              //process data for that client type and put it into the map of types
              clientTypeData = createClientTypeData(clientTypeIndex);
              clientTypesInitiated.put(clientTypeIndex, clientTypeData);
          }
        }
    }
    processClientUsingClientTypeData(clientTypeData);
}

Object createClientTypeData(int clientIndex) {return new Object();}

void processClientUsingClientTypeData(Object clientTypeData) {}

}

于 2012-09-18T19:30:17.830 に答える
1

ConcurrentHashMapは、同期ブロックを使用しないように作成されていますが、同期ブロックのロックとして使用することは問題ありませんが、効率的ではありません。を使用する必要がある場合ConcurrentHashMap、正しい使用法は次のとおりです。

private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>();

void onClientRequestReceived(int clientTypeIndex) {
    Object clientTypeData = clientTypesInitiated.get(clientTypeIndex);
    if (clientTypeData == null) {
        Object newClientTypeData = createClientTypeData(clientTypeIndex);
        clientTypeData = clientTypesInitiated.putIfAbsent(clientTypeIndex, newClientTypeData);
        // if clientTypeData is null, then put successful, otherwise, use the old one
        if (clientTypeData == null) {
            clientTypeData = newClientTypeData;
        }
    }
    processClientUsingClientTypeData(clientTypeData);
}

正直なところ、上記のコードはブロックclientTypeDataを使用していませんが、(競合状態のために)複数回作成される可能性があります。synchronizedしたがって、作成にかかる費用を測定する必要があります。また、ブロックclientTypeDataを使用しないと影が薄くなるほど費用がかかる場合は、synchronizedを使用する必要もありませんConcurrentHashMap。通常HashMapsynchronizedブロックを使用します。

于 2012-10-29T10:00:03.740 に答える
0

いいえ、そうではありません。メソッド全体をスレッドセーフにする必要がある場合は、同期済みとして宣言するか、同期ブロックを使用して重要なコードを配置します。

于 2012-09-18T19:20:35.817 に答える
0

ifとnextputの間でコンテキストスイッチが発生する可能性があるため、現在のコードはスレッドセーフではありません。clientTypesInitiatedインターフェイスMapではなく、タイプConcurrentHashMapとして作成する必要があります。次に、putIfAbsentメソッドを使用します

于 2012-09-18T19:29:24.960 に答える
0

コードが意図したとおりに機能しないと思います。見て:

void onClientRequestReceived(int clientTypeIndex) {
     // the line below ensures the existing data is lost even if was not null
    if (clientTypesInitiated.put(clientTypeIndex, "") == null) {
        Object clientTypeData = createClientTypeData(clientTypeIndex);
        clientTypesInitiated.put(clientTypeIndex, clientTypeData);
    } else {
        processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex));
    }
}

むしろ、getメソッド呼び出しが必要だったと思います。

私はそのようなアプローチを提案します:

void onClientRequestReceived(int clientTypeIndex) {
    Object clientTypeData;
    synchronized (clientTypesInitiated) {
        if ((clientTypeData = clientTypesInitiated.get(clientTypeIndex)) == null) {
            clientTypeData = createClientTypeData(clientTypeIndex);
            clientTypesInitiated.put(clientTypeIndex, clientTypeData);
        } 
    }
    processClientUsingClientTypeData(clientTypeData);
}
于 2012-09-18T19:29:46.220 に答える
0

putIfAbsentはうまく機能しますが、初期化を数回繰り返す必要がある場合があります。ダブルチェックされたロックを使用した同期ブロックは、おそらくこの問題を解決できます。

ただし、AtomicIntegerArraysに基づく別のオプションは次のとおりです。

public class Test {

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>();

    private static final int CLIENT_TYPES_NUMBER = 10;
    private static AtomicIntegerArray clientTypesInitStarted = new AtomicIntegerArray(CLIENT_TYPES_NUMBER);
    private static AtomicIntegerArray clientTypesInitFinished = new AtomicIntegerArray(CLIENT_TYPES_NUMBER);

    void onClientRequestReceived(int clientTypeIndex) {

        int isInitStarted = clientTypesInitStarted.getAndIncrement(clientTypeIndex);
        int isInitFinished = clientTypesInitFinished.get(clientTypeIndex);

        if (isInitStarted == 0) {
            //new client type index arrived, this type was never processed
            //process data for that client type and put it into the map of types
            Object clientTypeData = createClientTypeData(clientTypeIndex);
            clientTypesInitiated.put(clientTypeIndex, clientTypeData);
            clientTypesInitFinished.getAndIncrement(clientTypeIndex);
        } else if (isInitFinished > 0) {
            //already existing index - we already have results and we can use them
            processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex));
        }
    }

    Object createClientTypeData(int clientIndex) {
        return new Object();
    }

    void processClientUsingClientTypeData(Object clientTypeData) {
    }
}

何か意見はありますか?

于 2012-09-18T20:43:02.673 に答える