关于 Java 的 Map 计数问题

footmanff 发布于 2016/05/03 09:30
阅读 386
收藏 0

要做一个计数器,在并发环境下,对字符进行计数。如何保证线程安全且尽量高效

暂时只考虑单机情况。

以下代码是我做的测试,分别用了 HashMap,ConcurrentHashMap,ConcurrentHashMap + AtomicLong 去做测试。

结果显示 JDK 1.8 引入的 merge 方法确实挺好得解决问题,但是问题来了,如果线上环境是 1.7 那怎么处理这个问题?

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;

/**
 * Created by fm on 16/4/25.
 */
public class Main {

    private static Logger logger = LoggerFactory.getLogger("Main");

    int count = 100;

    public static void main(String[] args) {
        Main main = new Main();
        MapTest mapTest = new HashMapTest();
        main.test(mapTest);
        mapTest = new ConcurrentHashMapMergeTest();
        main.test(mapTest);
        mapTest = new AtomicLongMapMergeTest();
        main.test(mapTest);
    }

    public void test(final MapTest mapTest){
        long start = System.currentTimeMillis();
        final CountDownLatch countDownLatch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < count; j++) {
                            mapTest.add("key");
                        }
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            });
            t.start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            logger.error("error", e);
        }
        logger.info(mapTest.toString());
        logger.info("cost " + (System.currentTimeMillis() - start));
    }

    static interface MapTest {
        public void add(String key);
        public String toString();
    }

    static class HashMapTest implements MapTest{

        Map<String, Integer> map = new HashMap<>();

        public void add(String key){
            Integer value = map.get(key);
            if(value == null){
                map.put(key, 1);
            } else {
                map.put(key, value + 1);
            }
        }

        public String toString(){
            return map.toString();
        }
    }

    // since JDK 1.8
    static class ConcurrentHashMapMergeTest implements MapTest{
        Map<String, Integer> map = new ConcurrentHashMap<String, Integer>();

        @Override
        public void add(String key) {
            map.merge(key, 1, new BiFunction<Integer, Integer, Integer>() {
                @Override
                public Integer apply(Integer old, Integer param) {
                    return old + param;
                }
            });
        }

        public String toString(){
            return map.toString();
        }

    }

    // choice
    static class AtomicLongMapMergeTest implements MapTest{
        Map<String, AtomicLong> map = new ConcurrentHashMap<String, AtomicLong>();

        @Override
        public void add(String key) {
            AtomicLong l = map.get(key);
            if(l == null){
                map.put(key, new AtomicLong());
            } else {
                l.incrementAndGet();
            }
        }

        public String toString(){
            return map.toString();
        }
    }

}



加载中
返回顶部
顶部