编辑:我已经发现单例的构造函数被多次调用,因此看起来这些类被单独的类加载器多次加载。如何在Tomcat中创建全局单例?我一直在谷歌搜索,但到目前为止没有运气。
我有一个像我这样构造的单例对象:
private static volatile KeyMapper mapper = null;
public static KeyMapper getMapper()
{
if(mapper == null)
{
synchronized(Utils.class)
{
if(mapper == null)
{
mapper = new LocalMemoryMapper();
}
}
}
return mapper;
}
类KeyMapper基本上是HashMap的同步包装器,只有两个函数,一个用于添加映射,另一个用于删除映射。在我的32位Windows机器上运行Tomcat 6.24时,一切正常。但是,当在64位Linux机器(带有OpenJDK 1.6.0-b09的CentOS 5.4)上运行时,我添加一个映射并打印出KeyMapper使用的HashMap的大小,以验证添加的映射(即验证大小= 1)。然后我尝试用另一个请求检索映射,并且我一直变为null,当我检查HashMap的大小时它是0.我确信映射不会被意外删除,因为我已经注释掉所有要删除的调用(我不使用clear或任何其他mutators,只是获取和放置)。
请求通过Tomcat 6.24(配置为使用至少4个线程的200个线程)并且我将-Xnoclassgc传递给jvm以确保该类不会无意中收集垃圾(jvm也在-server中运行)模式)。我还为KeyMapper添加了一个finalize方法,以便在收集垃圾时验证它是否被垃圾收集。
我在我的智慧结束时,我无法弄清楚为什么一分钟HashMap中的条目存在而下一条不是:(
答案 0 :(得分:5)
另一个猜测:这两个请求是否可能由您的网络应用程序的不同副本提供?每个都在它自己的ClassLoader
中,因此具有不同的单身副本。
答案 1 :(得分:1)
您是否尝试删除外部检查
if(mapper == null)
{
因此总是点击同步点,它是微妙的东西,但可能你正在达到双重检查的锁定习语问题。描述here和许多其他文章。
必须承认我以前从未见过这个问题实际上咬人,但这听起来确实如此。
答案 2 :(得分:1)
使用这个解决方案,JVM保证它只有一个映射器,并且它在使用前已初始化。
public enum KeyMapperFactory {
;
private static KeyMapper mapper = new LocalMemoryMapper();
public static KeyMapper getMapper() {
return mapper;
}
}
答案 3 :(得分:1)
这可能不是您的问题的原因,但您正在使用错误的双重检查锁定。见这个,
http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
答案 4 :(得分:0)
我发现了一个相当差的修复方法。我将我的代码导出为JAR并将其放在$ TOMCAT / lib中并且有效。这显然是一个类加载器问题。
编辑:找出解决方案
好的,我终于找到了问题。
我通过添加到server.xml并将路径设置为“”使我的应用程序成为服务器的默认应用程序。但是,当我通过网址http://localhost/somepage.jsp访问某些内容时,以及其他内容的网址http://localhost/appname/anotherpage.jsp。
我将所有网址更改为使用http://localhost/而不是http://localhost/appname后问题得到解决。