安全地更新ConcurrentHashMap和AtomicInteger

时间:2011-11-21 17:00:47

标签: java concurrency concurrenthashmap

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

例如:假设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,然后将该字放在哈希映射中(如果不存在)。

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

4 个答案:

答案 0 :(得分:3)

不,不是。如果你有两个方法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。

答案 1 :(得分:3)

显然,另一个线程可以看到maxIndex递增然后被破坏。

假设这是地图上的所有内容(特别是没有删除),那么您可以尝试将该单词放在地图中,只有在成功时才会递增。

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

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

答案 2 :(得分:0)

AtomicInteger是你应该考虑使用的东西。

并且您应该将transaction块中需要发生的所有代码包装在synchronized(this)块中。

答案 3 :(得分:0)

其他答案是正确的---您班级中存在非线程安全字段。你应该做的,首先要确保

如何实施线程

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

2)找到任何访问器方法,确保每当修改全局对象的状态时(或者至少如果块已同步)它们是同步的。

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