简体   繁体   English

这个字典是否是线程安全的(ConcurrentHashMap + AtomicInteger)?

[英]Is this dictionary function thread-safe (ConcurrentHashMap+AtomicInteger)?

I need to write a really simple dictionary which will be append only. 我需要写一个非常简单的字典,它只会附加。 The dictionary will be shared between many threads. 字典将在许多线程之间共享。 When any thread calls getId I want to make sure the same id is always returned for the same word, ie there should be only one id for any unique word. 当任何线程调用getId我想确保始终为同一个单词返回相同的id,即任何唯一单词应该只有一个id。

Now obviously I could just synchronize access to the getId method, but that is not very fun. 现在显然我可以同步访问getId方法,但这不是很有趣。 So I wondered if there was a lock-free way to achieve this. 所以我想知道是否有一种无锁方式来实现这一目标。

In particular, I am wondering about the thread safety of using java.util.concurrent.ConcurrentHashMap#computeIfAbsent . 特别是,我想知道使用java.util.concurrent.ConcurrentHashMap#computeIfAbsent的线程安全性。 The javadoc for the interface ConcurrentMap says: ConcurrentMap接口的javadoc说:

The default implementation may retry these steps when multiple threads attempt updates including potentially calling the mapping function multiple times. 当多个线程尝试更新(包括可能多次调用映射函数)时,默认实现可能会重试这些步骤。

From that description, it is not clear to me if that means that the mapping function might be called more than once for the same key? 根据该描述,我不清楚这是否意味着对于同一个可能会多次调用映射函数?

If that is the case (ie the mapper might be called more than once for the same key), then I think the following code is most likely not thread-safe as it could call getAndIncrement more than once for the same key (ie word). 如果是这种情况(即映射器可能被多次调用同一个键),那么我认为以下代码很可能不是线程安全的,因为它可以为同一个键(即单词)调用getAndIncrement多次。

If that is not the case, then I think the following code is thread-safe. 如果不是这样,那么我认为以下代码是线程安全的。 Can anyone confirm? 谁能确认一下?

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();
    }  
}

This is guaranteed to be thread safe by the ConcurrentMap Javadoc (emphasis mine): 这保证是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. 在将对象作为键或值放入ConcurrentMap之前的线程中的操作发生在从另一个线程中的ConcurrentMap 访问或删除该对象之后的操作之前

The ConcurrentHashMap Javadoc has an example similar to yours: 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. 通过使用LongAdder值并通过computeIfAbsent初始化,ConcurrentHashMap可用作可伸缩频率映射(直方图或多集的形式)。 For example, to add a count to a ConcurrentHashMap<String,LongAdder> freqs , you can use freqs.computeIfAbsent(k -> new LongAdder()).increment(); 例如,要向ConcurrentHashMap<String,LongAdder> freqs添加计数,可以使用freqs.computeIfAbsent(k -> new LongAdder()).increment();

While this uses computeIfAbsent , it should be analogous to putIfAbsent . 虽然这使用computeIfAbsent ,但它应该类似于putIfAbsent

The java.util.concurrent package Javadoc talks about "happens-before": java.util.concurrent包Javadoc谈到“之前发生”:

Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. Java语言规范的第17章定义了内存操作的先发生关系,例如共享变量的读写。 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. 只有在读取操作之前发生写入操作 ,一个线程的写入结果才能保证对另一个线程的读取可见。

And the Language Specification says: 语言规范说:

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. 如果一个动作发生在另一个动作之前,那么第一个动作在第二个动作之前可见并且在第

So your code should be thread safe. 所以你的代码应该是线程安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM