线程安全映射:提高性能

时间:2014-07-19 15:54:19

标签: java concurrency synchronization

public class EntityUtils
{
    private static final Map<String, Map<String, String>> searchMap = new HashMap<>();

    private static Map<String, String> getSearchablePathMap(String key)
    {
        synchronized(searchMap)
        {
            Map<String, String> pathMap = searchMap.get(key);
            if(pathMap != null) return pathMap;

            pathMap = new HashMap<>();
            pathMap.put(..., ...);
            ...
            // heavy map population operations
            ...

            pathMap = Collections.unmodifiableMap(pathMap);

            searchMap.put(key, pathMap);
        }

        return pathMap;
    }
}

永远不会删除地图条目。

由于人口众多,无法使用ConcurrentHashMap.putIfAbsent()

您是否可以建议性能改进以避免key已经存在时同步?

3 个答案:

答案 0 :(得分:3)

无法避免某些级别的get同步,但可以允许并发读取。考虑使用ReadWriteLock来保护对搜索地图的访问权限。只有在得分远远超过看跌期权时才适用。

public class EntityUtils
{
    private static final ReadWriteLock RW_LOCK = new ReentrantReadWriteLock();
    private static final Map<String, Map<String, String>> searchMap = new HashMap<>();

    private static Map<String, String> getSearchablePathMap(String key)
    {
        RW_LOCK.readLock().lock();
        try
        {
            Map<String, String> pathMap = searchMap.get(key);
            if(pathMap != null) return pathMap;
        }
        finally
        {
            RW_LOCK.readLock().unlock();
        }

        RW_LOCK.writeLock().lock();
        try
        {
            //first check to see if a previous holder of write lock built map for us
            Map<String, String> pathMap = searchMap.get(key);
            if(pathMap != null) return pathMap;

            pathMap = new HashMap<>();
            pathMap.put(..., ...);
            ...
            // heavy map population operations
            ...

            pathMap = Collections.unmodifiableMap(pathMap);

            searchMap.put(key, pathMap);
        }
        finally
        {
            RW_LOCK.writeLock().unlock();
        }

        return pathMap;
    }
}

答案 1 :(得分:1)

您可以使用多个集合来最小化影响,但允许并发写入。

如果你使用ConcurrentHashMap,你甚至不需要锁定读取,只需写入。

public class EntityUtils {
    // any power of 2
    static final int CONCURRENCY = 16;
    static final EntityUtils[] EU = new EntityUtils[CONCURRENCY];
    static {
        for (int i = 0; i < CONCURRENCY; i++)
            EU[i] = new EntityUtils();
    }

    final Map<String, Map<String, String>> searchMap 
                                             = new ConcurrentHashMap<>();

    private static Map<String, String> getSearchablePathMap(String key) {
        int h = key.hashCode();
        int k = (h ^ (h >>> 16)) & (CONCURRENCY - 1);
        return EU[k].getSearchablePathMap0(key);
    }

    private Map<String, String> getSearchablePathMap0(String key) {
        Map<String, String> pathMap = searchMap.get(key);
        if(pathMap != null) return pathMap;

        synchronized(searchMap) {
            pathMap = searchMap.get(key);
            if(pathMap != null) return pathMap;

            pathMap = new HashMap<>();
            pathMap.put(..., ...);
            ...
            // heavy map population operations
            ...

            pathMap = Collections.unmodifiableMap(pathMap);

            searchMap.put(key, pathMap);
            return pathMap;
        }
    }
 }

答案 2 :(得分:0)

在密钥上建立锁定。

现在你要说可能有两个不同的String对象具有相同的值。是的,但是可以通过请求具有相同值的所有String对象的唯一实例来避免这种情况:

String uniqueKey = key.intern();

这可以避免整个Map上的锁定,但可以保证只有一个线程可以完成创建新映射条目的工作。

当然,地图上的锁定必须保留,但仅限于获取和放置的持续时间。