这个字典函数是线程安全的(ConcurrentHashMap+AtomicInteger)吗?
Is this dictionary function thread-safe (ConcurrentHashMap+AtomicInteger)?
我需要写一个非常简单的字典,它只能追加。字典将在多个线程之间共享。当任何线程调用时 getId
我想确保始终为同一个词返回相同的 ID,即任何唯一的词应该只有一个 ID。
现在显然我可以同步访问 getId
方法,但这不是很有趣。所以想知道有没有无锁的方法可以实现。
特别是,我想知道使用 java.util.concurrent.ConcurrentHashMap#computeIfAbsent
的线程安全性。接口 ConcurrentMap
的 javadoc 表示:
The default implementation may retry these steps when multiple threads attempt updates including potentially calling the mapping function multiple times.
根据该描述,我不清楚这是否意味着映射函数 可能 被多次调用 相同键?
如果是这种情况(即映射器可能会为同一个键调用多次),那么我认为以下代码很可能不是线程安全的,因为它可以多次调用 getAndIncrement
一次用于相同的键(即单词)。
如果不是这样,那么我认为下面的代码是线程安全的。谁能确认一下?
public class Dictionary {
private final AtomicInteger index = new AtomicInteger();
private final ConcurrentHashMap<String, Integer> words =
new ConcurrentHashMap<>();
public int getId(final String word) {
return words.computeIfAbsent(word, this::newId);
}
private int newId(final String word) {
return index.getAndIncrement();
}
}
ConcurrentMap
Javadoc(强调我的)保证线程安全:
Actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread.
ConcurrentHashMap
Javadoc 有一个与你的相似的例子:
A ConcurrentHashMap can be used as scalable frequency map (a form of histogram or multiset) by using LongAdder values and initializing via computeIfAbsent. For example, to add a count to a ConcurrentHashMap<String,LongAdder> freqs
, you can use freqs.computeIfAbsent(k -> new LongAdder()).increment();
虽然这使用了 computeIfAbsent
,但它应该类似于 putIfAbsent
。
java.util.concurrent
包 Javadoc 谈论 "happens-before":
Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.
语言规范说:
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.
所以你的代码应该是线程安全的。
我需要写一个非常简单的字典,它只能追加。字典将在多个线程之间共享。当任何线程调用时 getId
我想确保始终为同一个词返回相同的 ID,即任何唯一的词应该只有一个 ID。
现在显然我可以同步访问 getId
方法,但这不是很有趣。所以想知道有没有无锁的方法可以实现。
特别是,我想知道使用 java.util.concurrent.ConcurrentHashMap#computeIfAbsent
的线程安全性。接口 ConcurrentMap
的 javadoc 表示:
The default implementation may retry these steps when multiple threads attempt updates including potentially calling the mapping function multiple times.
根据该描述,我不清楚这是否意味着映射函数 可能 被多次调用 相同键?
如果是这种情况(即映射器可能会为同一个键调用多次),那么我认为以下代码很可能不是线程安全的,因为它可以多次调用 getAndIncrement
一次用于相同的键(即单词)。
如果不是这样,那么我认为下面的代码是线程安全的。谁能确认一下?
public class Dictionary {
private final AtomicInteger index = new AtomicInteger();
private final ConcurrentHashMap<String, Integer> words =
new ConcurrentHashMap<>();
public int getId(final String word) {
return words.computeIfAbsent(word, this::newId);
}
private int newId(final String word) {
return index.getAndIncrement();
}
}
ConcurrentMap
Javadoc(强调我的)保证线程安全:
Actions in a thread prior to placing an object into a ConcurrentMap as a key or value happen-before actions subsequent to the access or removal of that object from the ConcurrentMap in another thread.
ConcurrentHashMap
Javadoc 有一个与你的相似的例子:
A ConcurrentHashMap can be used as scalable frequency map (a form of histogram or multiset) by using LongAdder values and initializing via computeIfAbsent. For example, to add a count to a
ConcurrentHashMap<String,LongAdder> freqs
, you can usefreqs.computeIfAbsent(k -> new LongAdder()).increment();
虽然这使用了 computeIfAbsent
,但它应该类似于 putIfAbsent
。
java.util.concurrent
包 Javadoc 谈论 "happens-before":
Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.
语言规范说:
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.
所以你的代码应该是线程安全的。