2

目前我已经实现了 HashMap

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

并且 Item 是具有属性 Date expirationTime 和 byte[] data 的对象

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

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尚未完成为第一个线程写入数据]

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

4

2 回答 2

2

在 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 的多个线程不能都将相同的数据添加到缓存中。一个人会添加它,而其他线程将默默地忽略,因为缓存已经更新。

于 2011-07-07T16:10:17.683 回答
1

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

于 2011-07-07T16:05:24.617 回答