I had always assumed that a new instance of a Tag in was instantiated to service a request. I just found out the hard way that this is not the case (I assumed that all member variable objects were null on calling the tag - they aren't! they are left at whatever the previous thread set them to)
If a new instance is not created for each request, it follows that tags are not threadsafe either. In this case, is there any way to force the container to create multiple instances of the tags (as a javax.servlet.SingleThreadModel interface does for servlets)? Or do I need to rely on synchronizing?
Tags are not multithreaded, and don't need to be threadsafe, but neither can you assume that a new tag will be created for every request. The container may use tag pooling whereby a tag, once used, is retired into the pool to be re-used by a different request. The Tag javadoc outlines the lifecycle in detail. See also the release() method. In short, you should reset tag state in doEndTag() and release().
Actually you can get yourself into trouble resetting a tag.
The container is allowed to assume that it and it alone owns the values of the attributes. So let's say that in one instance of the tag you use xyz="213". If in the next instance you also use the same attribute with the same value, the container is not required to call the setter again. If you have reset the backing variable for the attribute, it will be as if the tag is ignoring the value.
Each container can act differently and still be spec-compliant. So you might get away with something in one, and run into a nightmare if porting to another. Resin, for example, is highly aggressive about optimizing its tag management (one of the reasons that it's so freakin' fast) and won't let you get away with some sloppiness that just may work fine in Tomcat. (Does this sound like I have the scars to prove it, or not?)
So tread lightly. Your best bet is to assume that the container owns the attributes and the instance variables that back them up, and keep hands off!
Internal instance variables not exposed to the container are yours to do with as you wish.