0

アイテムを取得/作成するために 2 つのわずかに異なるアプローチを使用していることに気付きましたが、ConcurrentHashMapどちらが優れているのだろうかと思います。

最初の方法:

public class Item {
  private boolean m_constructed;
  ...
  public void construct() {
    if (m_constructed) {
      return;
    }

    synchronized (this) {
      if (m_constructed) {
        return;
      }

      // Some heavy construction

      m_constructed = true;
    }
  }
}

ConcurrentHashMap<String, Item> m_items = new ConcurrentHashMap<String, Item>();

...
// The following code is executed concurrently by multiple threads:
public Item getOrCreateItem(String key) {
  Item newItem = new Item();                          // The constructor is empty
  Item item = m_items.putIfAbsent(key, newItem);
  if (item == null) {
    item = newItem;
  }
  item.construct();                                  // This is the real construction
  return item;
}

での使用についてコメントしないでくださいsynchronize (this)。ロックオブジェクトとして使用するのが悪いことは承知してthisいますが、この特定の例では問題ありません。

2 番目の方法:

public class Item {
  private boolean m_constructed;
  ...
  public void construct() {
    // Some heavy construction

    m_constructed = true;
  }

  public void waitForConstruction() throws InterruptedException {
    while (!m_constructed) {
      Thread.sleep(50);
    }
  }
}

ConcurrentHashMap<String, Item> m_items = new ConcurrentHashMap<String, Item>();

...
// The following code is executed concurrently by multiple threads:
public Item getOrCreateItem(String key) {
  Item newItem = new Item();                          // The constructor is empty
  Item item = m_items.putIfAbsent(key, newItem);
  if (item == null) {
    item.construct();                                 // This is the real construction
    item = newItem;
  }
  item.waitForConstruction();
  return item;
}

ある方法が他の方法よりも優れているのだろうか。何か案は?

編集

文脈について一言。Itemマップは複数のスレッドによって同時に設定され、そのすべてが実行されますgetOrCreateItem。他の方法でマップにアクセスしようとするコードはありません。ポピュレーションが終了すると、マップは変更されず、読み取り専用アクセスに開かれます。したがって、部分的に構築されたItemインスタンスをgetOrCreateItemメソッドの外で取得することはできません。

EDIT2

すべての答えをありがとう。提案された修正を含む最初のアプローチを採用しました。

public class Item {
  private volatile boolean m_constructed;         // !!! using volatile
  ...
  public void construct() {
    if (m_constructed) {
      return;
    }

    synchronized (this) {
      if (m_constructed) {
        return;
      }

      // Some heavy construction

      m_constructed = true;
    }
  }
}

ConcurrentHashMap<String, Item> m_items = new ConcurrentHashMap<String, Item>();

...
// The following code is executed concurrently by multiple threads:
public Item getOrCreateItem(String key) {
  Item item = m_items.get(key);                         // !!! Checking the mainstream case first
  if (item == null) {
    Item newItem = new Item();                          // The constructor is empty
    item = m_items.putIfAbsent(key, newItem);
    if (item == null) {
      item = newItem;
    }
  }
  item.construct();                                  // This is the real construction
  return item;
}

もちろん、getOrCreateItemメソッド以外の方法でアイテムのマップにアクセスするコードはないと仮定して動作しています。これは私のコードに当てはまります。

4

2 に答える 2

3

まず、どのソリューションも適切に同期されていないことに注意してください。これを機能させるには、m_constructed フラグを揮発性にする必要があります。そうしないと、このメンバーへの読み取りアクセスがロックによって保護されないため、スレッドの可視性の問題が発生する可能性があります。

とにかく、項目クラスがFutureの概念に似すぎている。Future の実装をマップ値として保存すると (例: FutureTask )、問題は解決します。

于 2013-05-04T12:50:05.043 に答える