java中的并发双向映射

时间:2012-07-13 19:12:37

标签: java concurrency hashmap concurrenthashmap

我正在编写用于文本处理的代码,如果我首先将字符串转换为整数,那么事情会更快很多。为了做到这一点,我创建了一个Dictionary类,每次看到一个新的字符串时,我给它一个索引,并保留两个映射,一个从string到int,一个从int到string,所以我可以很容易地查找两个方法。这是代码:

class Dictionary {
    private Map<String, Integer> map;
    private Map<Integer, String> reverse_map;
    private int nextIndex;

    public Dictionary() {
        map = new HashMap<String, Integer>();
        reverse_map = new HashMap<Integer, String>();
        nextIndex = 1;
    }

    public int getIndex(String string) {
        if (!map.containsKey(string)) {
            map.put(string, nextIndex);
            reverse_map.put(nextIndex, string);
            nextIndex++;
        }
        return map.get(string);
    }

    public String getString(int index) {
        // getIndex is always called first, so we don't need to check anything
        return reverse_map.get(index);
    }
}

在我的单线程代码中,这对我来说很好。但现在我想给这个多线程加速它,我不知道该怎么做。我想过使用ConcurrentHashMap,但我不确定putIfAbsent是否会保证我不会使用索引两次。我不想使用Collections.synchronizedMap,因为这个字典在线程中经常被访问,因此我可能不会比使用单个线程好多了,因为它会阻塞每次读写。有没有办法使这项工作?

2 个答案:

答案 0 :(得分:1)

您使用并发解决方案的问题是原子性。这些是我的想法:

private final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
private final ConcurrentMap<Integer, String> reverse_map = new ConcurrentHashMap<Integer, String>();
private final AtomicInteger nextIndex = new AtomicInteger(1);

public int getIndex(String string) {
  Integer i = map.get(string);
  if (i == null) {
    final Integer newI = nextIndex.getAndIncrement();
    i = map.putIfAbsent(string, newI);
    if (i == null) {
      reverse_map.put(newI, string);
      return newI;
    }
  }
  return i;
}

这有一个非常良性的失败模式:一些指数将被闲置。

请注意,我无条件地放入第二张地图,因为此时我知道我负责手头的字符串。

答案 1 :(得分:1)

最简单的方法是标记您的两种方法(getIndexgetStringsynchronized。看看你获得了什么样的加速。也许这就够了。

要使用ConcurrentHashMap,您可以尝试这样做:

private AtomicInteger nextIndex;
public int getIndex(String string) {
    Integer n = map.get(string);
    if (n == null) {
        int idx = nextIndex.getAndIncrement();
        n = map.putIfAbsent(string, idx);
        if (n != null) return n;
        reverse_map.put(idx, string);
        return idx;
    }
    return n;
}

如果两个线程同时插入相同的字符串,这可能偶尔会跳过索引,但它不会经常出现。