【问题标题】:HashMap is broken/ performance issuesHashMap 损坏/性能问题
【发布时间】:2011-07-07 15:54:47
【问题描述】:

目前我已经实现了 HashMap

private static Map<String, Item> cached = new HashMap<String, Item>();

并且 Item 是一个具有属性的对象 Date expireTime 和 byte[] 数据

当多个线程同时开始命中此映射时使用此映射。 我做的检查是

1.

public static final byte[] getCachedData(HttpServletRequest request) throws ServletException
{
    String url = getFullURL(request);
    Map<String, Item> cache = getCache(request);  // this chec
    Item item = null;

    synchronized (cache)
    {
        item = cache.get(url);
        if (null == item)
            return null;

        // Make sure that it is not over an hour old.
        if (item.expirationTime.getTime() < System.currentTimeMillis())
        {
            cache.remove(url);
            item = null;
        }
    }

    if (null == item)
    {
        log.info("Expiring Item: " + url);
        return null;
    }

    return item.data;
}

2。如果数据返回null,那么我们创建数据并将其缓存在hashMap中

public static void cacheDataX(HttpServletRequest request, byte[] data, Integer minutes) throws ServletException
{
    Item item = new Item(data);
    String url = getFullURL(request);
    Map<String, Item> cache = getCache(request);

    log.info("Caching Item: " + url + " - Bytes: " + data.length);
    synchronized (cache)
    {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MINUTE, minutes);
        item.expirationTime = cal.getTime();
        cache.put(url, item);
    }
}

似乎如果多个线程访问say键(在这种情况下为url),则数据会在同一键位置多次添加到缓存中[因为getCacheData将为多个线程返回null,因为hashmap尚未完成首先写入数据线程]

关于如何解决问题的任何建议?

【问题讨论】:

  • java.util.concurrent.ConcurrentHashMap; putIfAbsent 方法可能非常有用。
  • 日历是一个很重的物体。如果您查看 GregorianCalender 的来源,您就会明白为什么。尝试改用 System.currentTimeMillis()。

标签: java performance hashmap concurrenthashmap


【解决方案1】:

在 cacheDataX 中,在添加之前检查项目是否存在(在同步块内)。

synchronized (cache)
    {
        if (cache.get(url) == null) {
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.MINUTE, minutes);
            item.expirationTime = cal.getTime();
            cache.put(url, item);
        }
    }

这将确保已经完成查找并返回 null 的多个线程不能都将相同的数据添加到缓存中。一个人会添加它,其他线程会默默地忽略,因为缓存已经更新了。

【讨论】:

    【解决方案2】:

    您需要一个同步块,以涵盖从缓存中获取内容以及插入缓存的内容。正如代码所示,您有一个竞争条件:多个线程可以在任何人执行第 2 步之前执行第 1 步。

    【讨论】:

    • @jtoberon - 这与双重检查锁定无关。这是处理对象构造和延迟初始化的一个非常具体的习语。
    • 我将删除我之前的评论,因为它令人困惑。我的意思是,如果您希望只创建一件事,通常您必须在一个同步块中执行以下操作:“(1) 检查引用,(2) 如果为 null,则创建,(3) 设置引用。”双重检查和发布的代码都不能正确地做到这一点。
    猜你喜欢
    • 2012-03-05
    • 1970-01-01
    • 2011-07-14
    • 2010-12-13
    • 1970-01-01
    • 2017-03-25
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多