繁体   English   中英

安全地更新ConcurrentHashMap和AtomicInteger

[英]Updating both a ConcurrentHashMap and an AtomicInteger safely

我必须将单词及其对应的整数索引存储在哈希图中。 哈希图将同时更新。

例如:假设wordList{a,b,c,a,d,e,a,d,e,b}哈希图将包含以下键值对

a:1
b:2
c:3
d:4
e:5

的代码如下:

public class Dictionary {

private ConcurrentMap<String, Integer>  wordToIndex;
private AtomicInteger                   maxIndex;

public Dictionary( int startFrom ) {
    wordToIndex = new ConcurrentHashMap<String, Integer>();
    this.maxIndex = new AtomicInteger(startFrom);
}


public void insertAndComputeIndices( List<String> words ) {

    Integer index;
    //iterate over the list of words
    for ( String word : words ) {
        // check if the word exists in the Map
        // if it does not exist, increment the maxIndex and put it in the
        // Map if it is still absent
        // set the maxIndex to the newly inserted index

        if (!wordToIndex.containsKey(word)) {
            index = maxIndex.incrementAndGet();

            index = wordToIndex.putIfAbsent(word, index);
            if (index != null)
                maxIndex.set(index);
        }
    }
}

我的问题是上面的类是否是线程安全的? 基本上,在这种情况下,原子操作应该是增加maxIndex ,如果不存在该单词,则将该单词放入哈希图中。

在这种情况下是否有更好的方法来实现并发?

不它不是。 如果您有两个方法A和B,这两个方法都是线程安全的,那么这当然并不意味着在序列中调用A和B也是线程安全的,因为线程可以在函数调用之间中断另一个方法。 这是这里发生的情况:

    if (!wordToIndex.containsKey(word)) {
        index = maxIndex.incrementAndGet();

        index = wordToIndex.putIfAbsent(word, index);
        if (index != null)
            maxIndex.set(index);
    }

线程A验证wordToIndex不包含单词“ dog”,并在if内进行。 在可以添加单词“ dog”之前,线程B还发现“ dog”不在映射中(A尚未添加),因此它也在if内进行。 现在,您尝试将单词“ dog”插入两次。

当然,putIfAbsent将保证只有一个线程可以添加它,但是我认为您的目标是不要让两个线程同时使用相同的键输入if。

显然,另一个线程可以看到maxIndex递增,然后变得maxIndex

假设这就是地图上正在进行的所有操作(特别是没有删除操作),那么您可以尝试将单词放入地图中,并且仅在成功的情况下才递增。

    Integer oldIndex = wordToIndex.putIfAbsent(word, -1);
    if (oldIndex == null) {
        wordToIndex.put(word, maxIndex.incrementAndGet());
    }

(或者对于单个put ,使用某种可变类型代替Integer 。)

您应该考虑使用AtomicInteger

然后,您应该将所有需要作为transaction发生的代码包装在一块synchronized(this)块中。

其他答案是正确的---您的课程中有非线程安全字段。 首先,您应该做的是确保

如何实现线程

1)我将确保内部所有内容都是私有的,尽管这不是线程安全代码的要求。

2)找到您的任何访问器方法,确保每当修改全局对象的状态时它们都被同步(或者至少在同步块的情况下)。

3)测试死锁或错误计数,这可以通过在10000个线程插入后确保maxIndex的值正确来在单元测试中实现,例如...

暂无
暂无

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

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