多线程环境中缓存集合上的无限循环

时间:2011-09-09 14:21:05

标签: java multithreading collections ehcache hashset

我的应用程序使用Spring和Hibernate框架在tomcat上运行。它使用EHCache作为服务级别的缓存提供程序。这意味着服务类创建的对象将被放入缓存中。 (不是休眠Dao对象)。

这些缓存对象中有一些集合对象(HashSet,ArrayList,HashMap)。不是它们是同步集合。所有这些都不是线程安全的,但在将它们放入缓存后,它们不会被应用程序修改。

当我遍历这个集合时,我在很多场合都发现了无限循环。一些循环是Iterator循环,而另一些循环是旧循环运行基于int索引。

我设法通过用Collections.synchronizedSet(new HashSet())替换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;
}

1 个答案:

答案 0 :(得分:1)

EHCache不会以任何方式修改您的对象。有一个例外:如果你有一个基于磁盘的缓存(即可以溢出到磁盘的缓存),那么EHCache将序列化你的对象,将它们写入磁盘并在需要时再次加载它们。

因此,如果序列化您的对象时出现问题,您已将EHCache配置为溢出,这可能会导致问题但不会出现问题。

我的猜测是,几个具有相同ID的对象被放入缓存中,或者在完全初始化之前将对象添加到缓存中。

如何调试?

  1. 如果有人在将集合添加到缓存后尝试修改集合,请使用Collections.unmodifiable*()来获取错误。

  2. 保存集合的hashCode()并验证它。如果a)集合发生更改,则hashCode()会发生更改; b)如果集合中对象的hashCode()发生更改,则{{1}}会发生更改。

  3. 特别是后者是不可预见问题的一个很好的来源:人们在hashCode()中使用非final字段,将对象添加到集合/映射中,并发生奇怪的事情。