0

この以下のコードの何が問題になっていますか?.

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();    
Record rec = records.get(id);
  if (rec == null) {
      rec = new Record(id);
      records.put(id, rec);
  }
  return rec;
  1. 上記のコードはスレッドセーフではありませんか? この場合、なぜputIfAbsentここで使用する必要があるのですか?
  2. ロックは更新にのみ適用されます。検索の場合、完全な同時実行が可能です。このステートメントはどういう意味ですか?.
4

2 に答える 2

6

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

  1. 別のスレッドがあった場合、その間records.getrecords.put他のスレッドもレコードを配置した可能性があります。

  2. 読み取り専用操作 (つまり、構造を変更しない操作) は、複数のスレッドで同時に実行できます。たとえば、1000 個のスレッドが の値を安全に読み取ることができますint。ただし、これらの 1000 のスレッドは、intなんらかのロック操作なしでは の値を更新できません。

これは非常にありそうもないイベントのように聞こえるかもしれませんが、100 万分の 1 のイベントが 1GHz で毎秒 1000 回発生することを思い出してください。


これはスレッドセーフです:

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
// presumably aMap is a member and the code below is in a function
aMap.putIfAbsent(id, new Record(id))
Record rec = records.get(id);
return rec;

これにより が作成され、Record使用されない可能性があることに注意してください。

于 2013-09-29T00:30:48.583 に答える
3

どのように動作させたいかによって、スレッドセーフになる場合とそうでない場合があります。

コードの最後までに、aMap安全にRecordfor がありidます。ただし、2 つのスレッドが作成と挿入の両方Recordを行い、2 つ (またはそれ以上のスレッドが実行する場合はそれ以上)Recordsが存在する可能性があります。それは問題ないかもしれませんし、そうではないかもしれません - 本当にあなたのアプリケーションに依存します。

スレッド セーフの危険性の 1 つ (たとえば、HashMap同期なしで法線を使用する場合) は、スレッド間で部分的に作成または部分的に更新されたオブジェクトをスレッドが読み取ることができることです。言い換えれば、物事は本当に混乱する可能性があります。スレッド間でメモリが最新の状態に保たれるため、これはコードでは発生しません。その意味で、スレッドセーフです。ConcurrentHashMap

できることの 1 つは を使用することですputIfAbsent。これは、キーと値のペアをアトミックにマップに配置しますが、そのキーにまだ何もない場合に限ります。

if (rec == null) {
    records.putIfAbsent(id, new Record(id));
    rec = records.get(id);
}

このアプローチでは、2 番目のRecordオブジェクトを作成することもできますが、その場合、オブジェクトは挿入されず、すぐにガベージ コレクションに使用できるようになります。スニペットの終わりまでに:

  • records指定されたRecordID の
  • RecordそのIDに入れられたのは1つだけrecordsです(このスレッドまたは別のスレッドによってそこに入れられたかどうかにかかわらず)
  • recそのレコードを指します
于 2013-09-29T00:48:13.763 に答える