アイテムを取得/作成するために 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
メソッド以外の方法でアイテムのマップにアクセスするコードはないと仮定して動作しています。これは私のコードに当てはまります。