开发者

Infinite Loop on Cached collection in Multithreaded environment

My application is running on tomcat with Spring and Hibernate framework. It uses EHCache as a cache provider in service level. That means objects created by service classes are put into the cache. (Not hibernate Dao objects).

There are some collection objects (HashSet, ArrayList, HashMap) in these cached objects. Non of them are synchronized collections. All are not thread safe, but they are not modified by the application after they are put into the cache.

I found infinite loop in many occasions when I loop through this collection. Some of the loops are Iterator loops and some others are old for loops run base on int index.

I managed to overcome one infinite loop by replacing a HashSet with Collections.synchronizedSet(new HashSet()). But I don't understand the real problem of using normal HashSet as it's never being modified by the application. (Does EHCache modify them?)

Please explain to me if there are any concerns here using non-thread safe collections.

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

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

Following loop run infinitely and blow out the heap

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);
}
}

once replace the HashSet, the problem solved

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

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

And this is another one

private int getRatesStartIndex(GsRoomRate gsRoomRate, List<GsRate开发者_如何学Python> 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;
}


EHCache doesn't modify your objects in any way. There is one exception: If you have a disk-based cache (i.e. a cache that can overflow to disk), then EHCache will serialize your objects, write them to disk and load them again if they are needed.

So if there is a problem serializing your objects and you have configured EHCache to spill over, this could cause problems but it doesn't feel like your issue.

My guess is that several objects with the same ID are put into the cache or that objects are added to the cache before they are completely initialized.

How to debug this?

  1. Use Collections.unmodifiable*() to get errors if someone tries to modify a collection after having added it to the cache.

  2. Save the hashCode() of the collection and validate it. The hashCode() changes if a) the collection changes or b) if the hashCode() of an object in the collection changes.

Especially the latter is a nice source of unforeseen problems: People use non-final fields in hashCode(), add objects to sets/maps and odd things happen.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜