Accessing a global variable on a multithreaded Tomcat server
Edit: I realized that the constructor for the singleton gets multiple times, so it seems that the classes are loaded more than once by separate classloaders. How can I make a global singleton in Tomcat? I've been googling but no luck so far.
I have a singleton object that I create like this:
private static volatile KeyMapper mapper = null;
public static KeyMapper getMapper()
{
if(mapper == null)
{
synchronized(Utils.class)
{
if(mapper == null)
{
mapper = new LocalMemoryMapper();
}
}
}
return mapper;
}
The KeyMapper class is basically a synchronized wrapper for HashMap with two functions, one for adding a mapping and one for removing a mapping. When running in Tomcat 6.24 on my Windows 32 bit machine, everything works fine. However, when running on a 64-bit Linux machine (CentOS 5.4 with OpenJDK 1.6.0-b09) I add one mapping and print the size of the HashMap used by KeyMapper to check the added mapping (i.e. Check size = 1). Then I try to get a mapping from another query and I still get null and when I checked the size of the HashMap it was 0. I'm sure the mapping is not accidentally deleted since I commented out all the calls to delete (and I am not using the clear or any other mutators, just get it and put it on).
The requests go through Tomcat 6.24 (configured to use 200 threads with a minimum of 4 threads) and I passed -Xnoclassgc to jvm to ensure that the class doesn't get garbage collected accidentally (jvm also runs on server -server Mode). I also added a finalize method for KeyMapper to print to stderr if ever garbage collected, to make sure it wasn't garbage collected.
I'm on my way and can't figure out why there is an entry in the HashMap for one minute, but the next one is not :(
a source to share
I found a pretty bad solution. I exported my code as JAR and put it in $ TOMCAT / lib and it worked. This is clearly a class loader problem.
Edit: Computed solution
Ok, I finally figured out the problem.
I made a default server app app by adding it to server.xml and setting the path to ". However, when I accessed it via the URL http: //localhost/somepage.jsp for something, but also the URL http: //localhost/appname/anotherpage.jsp for other things.
Once I changed all urls to use http: // localhost / instead of http: // localhost / appname, the problem was fixed.
a source to share
Have you tried to remove external validation
if(mapper == null)
{
So, always hitting the Synchronized point is subtle stuff, but you might be running into a double blocking idiom problem. Described here and in many other articles.
I must admit that I've never seen a problem actually bite someone before, but it looks exactly like this.
a source to share
This may not be the cause of your problem, but you are using a bad double checked lock. See this.
http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
a source to share