如何避免ConcurrentHashMap的使用

时间:2014-06-25 22:14:01

标签: java hadoop

我已经在Hadoop

中的Reducer类的run()方法中编写了这段代码
@Override
    public void run(Context context) throws IOException, InterruptedException {
        setup(context);

        ConcurrentHashMap<String, HashSet<Text>> map = new ConcurrentHashMap<String, HashSet<Text>>();

        while (context.nextKey()) {
            String line = context.getCurrentKey().toString();
            HashSet<Text> values = new HashSet<Text>();
            for (Text t : context.getValues()) {
                values.add(new Text(t));
            }

            map.put(line, new HashSet<Text>());
            for (Text t : values) {
                map.get(line).add(new Text(t));
            }
        }

        ConcurrentHashMap<String, HashSet<Text>> newMap = new ConcurrentHashMap<String, HashSet<Text>>();

        for (String keyToMerge : map.keySet()) {
            String[] keyToMergeTokens = keyToMerge.split(",");
            for (String key : map.keySet()) {
                String[] keyTokens = key.split(",");
                if (keyToMergeTokens[keyToMergeTokens.length - 1].equals(keyTokens[0])) {
                    String newKey = keyToMerge;
                    for (int i = 1; i < keyTokens.length; i++) {
                        newKey += "," + keyTokens[i];
                    }
                    if (!newMap.contains(newKey)) {
                        newMap.put(newKey, new HashSet<Text>());
                        for (Text t : map.get(keyToMerge)) {
                            newMap.get(newKey).add(new Text(t));
                        }
                    }
                    for (Text t : map.get(key)) {
                        newMap.get(newKey).add(new Text(t));
                    }
                }
            }


        //call the reducers
        for (String key : newMap.keySet()) {
            reduce(new Text(key), newMap.get(key), context);
        }

        cleanup(context);
    }

我的问题是,即使我的输入太小,由于 newMap.put()调用,因此需要30分钟才能运行。如果我将此命令放在注释中,那么它会快速运行而不会出现任何问题 如您所见,我使用ConcurrentHashMap。我不想使用它,因为我认为run()只在每台机器上调用一次(它不会同时运行)所以我对简单的HashMap没有任何问题,但如果我更换了concurrentHashMap与简单的HashMap我收到一个错误(concurrentModificationError)。 有没有人知道如何使它工作没有任何延迟? 提前谢谢!

*的Java6 * hadoop 1.2.1

1 个答案:

答案 0 :(得分:2)

我不知道它是否会解决您的性能问题,但我看到您正在做的一件效率低下的事情:

newMap.put(newKey, new HashSet<Text>());
for (Text t : map.get(keyToMerge)) {
    newMap.get(newKey).add(new Text(t));
}

将HashSet保存在变量中而不是在newMap中搜索它会更有效:

HashSet<Text> newSet = new HashSet<Text>();
newMap.put(newKey, newSet);
for (Text t : map.get(keyToMerge)) {
    newSet.add(new Text(t));
}

您正在做的另一个低效的事情是创建一个值的HashSet,然后创建另一个相同的HashSet以放入地图中。由于原始的HashSet(values)从未再次使用过,因此您无需任何理由构建所有这些Text对象。

而不是:

    while (context.nextKey()) {
        String line = context.getCurrentKey().toString();
        HashSet<Text> values = new HashSet<Text>();
        for (Text t : context.getValues()) {
            values.add(new Text(t));
        }

        map.put(line, new HashSet<Text>());
        for (Text t : values) {
            map.get(line).add(new Text(t));
        }
    }

您可以简单地写一下:

    while (context.nextKey()) {
        String line = context.getCurrentKey().toString();
        HashSet<Text> values = new HashSet<Text>();
        for (Text t : context.getValues()) {
            values.add(new Text(t));
        }

        map.put(line, values);
    }

编辑:

我刚刚看到您发布的其他代码作为答案(来自您的cleanup()方法):

    //clear map
    for (String s : map.keySet()) {
        map.remove(s);
    }
    map = null;

    //clear newMap
    for (String s : newMap.keySet()) {
        newMap.remove(s);
    }
    newMap = null;

此代码为您提供ConcurrentModificationError的原因是foreach循环不支持修改您正在迭代的集合。

要解决此问题,您可以使用迭代器:

    //clear map
    Iterator<Map.Entry<String, HashSet<Text>>> iter1 = map.entrySet ().iterator ();
    while (iter1.hasNext()) {
        Map.Entry<String, HashSet<Text>> entry = iter1.next();
        iter1.remove();
    }
    map = null;

    //clear newMap
    Iterator<Map.Entry<String, HashSet<Text>>> iter2 = newMap.entrySet ().iterator ();
    while (iter2.hasNext()) {
        Map.Entry<String, HashSet<Text>> entry = iter2.next();
        iter2.remove();
    }
    newMap = null;

那就是说,你真的不必单独删除每个项目。 你可以简单地写

map = null;
newMap = null;

当您删除对地图的引用时,垃圾收集器可以对它们进行垃圾收集。从地图中删除项目没有任何区别。