2

一意のキー、つまり 1(a,b) 2(c,d) などに対して複数の値を追跡する必要があります...

ソリューションは複数のスレッドによってアクセスされるため、効果的に次のように定義しています。

ConcurrentSkipListMap<key, ConcurrentSkipListSet<values>>

私の質問は、値セットのサイズが 0 の場合のキーの削除を同期する必要があるかどうかです。2 つのクラスが「同時」であることはわかっており、OpenJDK ソース コードを確認しましたが、Set が空であることを確認し、remove(...) で Map を削除する 1 つのスレッド T1 の間にウィンドウがあるように見えます。別のスレッド T2 が add(...) を呼び出しています。その結果、T1 は最後の Set エントリを削除し、Set エントリを追加するだけで T2 とインターリーブされた Map を削除します。したがって、マップと T2 セットのエントリは T1 によって削除され、データは失われます。

add() メソッドと remove() メソッドを「同期」するだけですか、それとも「より良い」方法がありますか?

Map は複数のスレッドによって変更されますが、2 つの方法でのみ変更されます。

コード スニペットは次のとおりです。

protected static class EndpointSet extends U4ConcurrentSkipListSet<U4Endpoint> {
    private static final long serialVersionUID = 1L;
    public EndpointSet() {
        super();
    }
}

protected static class IDToEndpoint extends U4ConcurrentSkipListMap<String, EndpointSet> {
    private static final long serialVersionUID = 1L;
    protected Boolean add(String id, U4Endpoint endpoint) {
        EndpointSet endpoints = get(id);
        if (endpoints == null) {
            endpoints = new EndpointSet();
            put(id, endpoints);
        }
        endpoints.add(endpoint);
        return true;
    }

    protected Boolean remove(String id, U4Endpoint endpoint) {
        EndpointSet endpoints = get(id);
        if (endpoints == null) {
            return false;
        } else {
            endpoints.remove(endpoint);
            if (endpoints.size() == 0) {
                remove(id);
            }
            return true;
        }
    }
}
4

1 に答える 1

0

あなたのコードにはデータ競合があります。発生する可能性のある例:

  • if (endpoints.size() == 0)との間にスレッドを追加できremove(id);ます-あなたはそれを見ました
  • ではadd、スレッドが null 以外の値を読み取り、EndpointSet endpoints = get(id);別のスレッドがそのセットからデータを削除する可能性があり、セットが空であるためマップからセットを削除する可能性があります。その後、最初のスレッドはセットに値を追加しますが、これはもはやマップに保持されません => 到達不能になるとデータも失われます。

問題を解決する最も簡単な方法は、 add と remove の両方を作成することsynchronizedです。ただし、ConcurrentMap.

または、メモリの制約がない限り、マップに空のセットをそのままにしておくこともできます。なんらかの形式の同期が必要ですが、最適化する方が簡単です。

競合 (パフォーマンス) が問題になる場合は、キーまたは値を同期することで、よりきめ細かいロック戦略を試すことができますが、非常に難しい場合があります (また、文字列プーリングのため、文字列のロックはあまりお勧めできません)。

すべての場合において、外部で自分で同期する必要があるため、非同時セットを使用できるようです。

于 2012-08-15T10:49:17.163 に答える