我发现自己使用两种略有不同的方法从/中获取/创建项目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
用作锁定对象的糟糕之处,但在这个特定的示例中我很好。
第二种方式:
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
。
编辑2
感谢所有的答案。我采用了第一种方法和建议的修复:
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
。这在我的代码中是正确的。