HashMap не работает / проблемы с производительностью - PullRequest
2 голосов
/ 07 июля 2011

В настоящее время у меня есть HashMap, который

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

и Item является объектом со свойствами Дата истечения времени и байта [] данных

Эта карта используется, когда несколько потоков одновременно начинают попадать в нее. Чек, который я делаю,

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. Если данные возвращаются нулевые, то мы создаем и кешируем их в 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 еще не завершил запись данных для первого нить]

Есть предложения, как решить проблему?

Ответы [ 2 ]

2 голосов
/ 07 июля 2011

В 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, не могут все добавить одни и те же данные в кэш. Один из них добавит его, а другие потоки будут игнорировать, поскольку кэш уже обновлен.

1 голос
/ 07 июля 2011

Вам нужен один блок синхронизации, чтобы покрыть как получение чего-то из кеша, так и вставку в кеш.Поскольку код стоит, у вас есть условие состязания: несколько потоков могут выполнить шаг 1, прежде чем кто-либо выполнит шаг 2.

...