Бесконечный цикл для кэшированной коллекции в многопоточной среде

Мое приложение работает на tomcat с фреймворком Spring и Hibernate. Он использует EHCache в качестве поставщика кэша на уровне обслуживания. Это означает, что объекты, созданные классами обслуживания, помещаются в кэш. (Не вводить в спящий режим объекты Дао).

В этих кэшированных объектах есть несколько объектов коллекции (HashSet, ArrayList, HashMap). Ни одна из них не является синхронизированной коллекцией. Все они не являются потокобезопасными, но они не изменяются приложением после помещения в кеш.

Я находил бесконечный цикл во многих случаях, когда просматривал эту коллекцию. Некоторые из циклов являются циклами итераторов, а некоторые другие — старыми циклами for, которые запускаются на основе индекса int.

Мне удалось преодолеть один бесконечный цикл, заменив HashSet на Collections.synchronizedSet(new HashSet()). Но я не понимаю реальной проблемы использования обычного HashSet, поскольку приложение никогда не модифицирует его. (Изменяет ли их EHCache?)

Пожалуйста, объясните мне, есть ли какие-либо проблемы с использованием коллекций, не являющихся потокобезопасными.

public class HotelDetails implements Serializable { /*Objects in the cache */
private static final long serialVersionUID = 1L;
.....

private Set<String> facilities = new HashSet<String>();
}

Следующий цикл работает бесконечно и выдувает кучу

if (hotelDetails.getFacilities() != null && hotelDetails.getFacilities().size() > 0) {
for (String fac : hotelDetails.getFacilities()) {
    TFacility f = of.createTFacility();
    f.setCode(fac);
    f.setValue(fac);
    facilities.getFacility().add(f);
}
}

после замены HashSet проблема решена

public class HotelDetails implements Serializable { /*Objects in the cache */
private static final long serialVersionUID = 1L;
.....

private Set<String> facilities = Collections.synchronizedSet(new HashSet<String>());
}

А это еще один

private int getRatesStartIndex(GsRoomRate gsRoomRate, List<GsRate> gsRates, Date travelStart) {
    Integer startIndex = gsRoomRate.getGsRateIndexes().get(travelStart);
    if (startIndex==null) {
        for (startIndex=0; startIndex<gsRates.size(); startIndex++) {
            GsRate gsRate = gsRates.get(startIndex);
            if (travelStart.between(gsRate.getStartDate(), gsRate.getEndDate())) {
                gsRoomRate.getGsRateIndexes().put(travelStart, startIndex);
                break;
            }
        }
        if (startIndex>=gsRates.size()) startIndex = 0;
    }

    return startIndex;
}


public class GsRoomRate implements Serializable { /*Objects in the cache */
    private static final long serialVersionUID = 1L;
    private List<GsRate> gsRates = new ArrayList<GsRate>();
    private Map<Date, Integer> gsRateIndexes = new HashMap<Date, Integer>();
}

public class GsRate implements Serializable { /*Objects in the cache */

    private static final long serialVersionUID = 1L;

    private RBADate startDate;
    private RBADate endDate;
}

person Rasika    schedule 09.09.2011    source источник
comment
После перехода на синхронизированные наборы ваши звонки возвращаются? Как понять, что вы попали в бесконечный цикл? Кажется, вы указали, что думаете, что находитесь в бесконечном цикле, потому что вам не хватает места в куче...?   -  person Matt Crinklaw-Vogt    schedule 09.09.2011


Ответы (1)


EHCache никак не изменяет ваши объекты. Есть одно исключение: если у вас есть дисковый кеш (то есть кеш, который может переполняться на диск), то EHCache сериализует ваши объекты, записывает их на диск и снова загружает, если они нужны.

Таким образом, если есть проблема с сериализацией ваших объектов, и вы настроили EHCache для переноса, это может вызвать проблемы, но это не похоже на вашу проблему.

Я предполагаю, что несколько объектов с одинаковым идентификатором помещаются в кеш или объекты добавляются в кеш до того, как они полностью инициализируются.

Как это отладить?

  1. Используйте Collections.unmodifiable*() для получения ошибок, если кто-то попытается изменить коллекцию после ее добавления в кеш.

  2. Сохраните hashCode() коллекции и подтвердите ее. hashCode() изменяется, если а) изменяется коллекция или б) если изменяется hashCode() объекта в коллекции.

В частности, последнее является хорошим источником непредвиденных проблем: люди используют неконечные поля в hashCode(), добавляют объекты в наборы/карты и происходят странные вещи.

person Aaron Digulla    schedule 09.09.2011
comment
Спасибо, Аарон... После еще одного расследования выяснилось, что HashMap вызывает проблему. Внутренний цикл метода get() работает бесконечно. Можно найти много информации по этому вопросу. Но не прямые ответы. Безопасно ли использовать HashMap в многопоточном приложении, если HashMap не будет изменяться? - person Rasika; 13.09.2011
comment
Да, все структуры Java безопасны, если они не изменены. Ваше описание звучит так, будто поток A добавляет данные, а поток B получает данные с карты. Это может вызвать всевозможные проблемы. Попробуйте ConcurrentHashMap, чтобы увидеть, решит ли это проблему. Или Collections.unmodifiable*() согласно моему ответу выше. - person Aaron Digulla; 28.09.2011