Re: unable to synchronize on primitive wrapper class?
Eniac Zhang wrote:
You're welcome, but please learn not to top-post.
As Eric pointed out, this is not a bug but a feature: Integer objects
are immutable by design and this is widely documented. The behavior
of autoboxing is also documented. You might not like it, but it isn't
a bug if things behave the way you're told they will.
Thanks, two lessons learned:
1. not to top post.
2. Don't synchronize on primitive type wrapper classes as you don't know
when those object will be replaced rather than changed under the hood.
Your understanding of (1) seems fine, but I think there's
some room for improvement with (2).
The "thing" you synchronize on is an object instance -- even
when you synchronize a static method, you synchronize on its
Class object. So, how do you specify which of the many objects
in the program you want to synchronize on? You can just write
synchronized, and this is shorthand for synchronized(this)or for
synchronized(something.class), depending on context. Or you can
write synchronized(someObject) to designate the chosen object
explicitly.
But what is someObject? It's a *reference* to an object
instance, not the object itself. At different times in the
program, the same reference variable can point to many different
object instances; more generally, the same reference expression
can point to many different object instances. The one object
instance you synchronize on is the one to which someObject
points at the time you enter the synchronized(someObject) block.
If someObject used to point to some other object, or will in
the future point to some other object, it makes no difference:
you are synchronized on the particular object it pointed to at
the chosen moment, no matter what happens later.
So, what has all this to do with primitive wrapper classes?
The only special thing about wrappers is that the compiler "knows"
about them and allows you to apply ordinary arithmetic operators
to them instead of writing method calls. The calls are still
there in the code, but the compiler writes them for you. The
problem in your code was that you didn't realize `someObject++'
would actually cause someObject to refer to a different object
instance -- so when you synchronized on the original object,
then changed someObject to point to a new one, you wound up
synchronized on the wrong thing. It's just as if you had done
MutableObject someObject = MutableObject.getInstance(1);
synchronized(someObject) {
someObject = MutableObject.getInstance(2);
someObject.dangerousMethod();
}
Since you're synchronized on someObject, it must be safe to call
someObject.dangerousMethod(), right? Wrong: someObject refers
to different objects at different times, you're synchronized on
one of those objects, but you call dangerousMethod() on the
other one.
--
Eric Sosman
esosman@ieee-dot-org.invalid