2

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

例如:假设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);
        }
    }
}

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

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

4

4 回答 4

3

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

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

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

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

于 2011-11-21T17:11:44.330 回答
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 中继续。现在您尝试插入两次“狗”这个词。

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

于 2011-11-21T17:04:18.307 回答
0

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

你应该将所有需要发生的代码包装transaction在一个synchronized(this)块中。

于 2011-11-21T17:19:31.290 回答
0

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

如何实现线程

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

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

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

于 2011-11-21T17:25:13.343 回答