hashCode() ?

T

Thomas Hawtin

Roedy said:
An hashcode is assigned in the constructor. Only one thread can
possibly see an object as it is being constructed.

Actually there were two thread-safety bugs in the initial example code,
but one wouldn't usually be considered as such.

The important problem is the access to the static field. Two objects
could be created at the same time. That could lead to the second
thread's constructor run loading a value between the first's load and store.

The second problem is that Java does not guarantee anything remotely
like Sequential Consistency. You may create an object and then store a
reference to it. But there is not a guarantee that another thread will
see it in the same order. Prior to 1.4 you could ensure correct
behaviour with client code that recklessly uses objects of your class
between threads without correct synchronisation. However you would
require a synchronized block in both the constructor (very rare) and the
hashCode method. From 1.5 you can just define the field final (and make
sure not to leak this from the constructor).


Sun's implementation of Object.hashCode appears to assign the identity
hashcode lazily. Presumably to keep the allocation code as short as
possible.

Tom Hawtin
 
T

Thomas Hawtin

^^i.e. Object.hashCode/System.identityHashCode
If you held onto all your objects you could get uniqueness.

Not true. Holding onto objects does not help much. The code below has
references to two objects with the same identity hash code simultaneously.

The address isn't used raw, as that produces hash codes with the three
lowest bits zero.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6321873

class HashClash {
public static void main(String[] args) {
final Object obj = new Object();
final int target = obj.hashCode();
Object clash;
long ct = 0;
do {
clash = new Object();
++ct;
} while (clash.hashCode() != target && ct<10L*1000*1000*1000L);
if (clash.hashCode() == target) {
System.out.println(ct+": "+obj+" - "+clash);
} else {
System.out.println("No clashes found");
}
}
}

62028120: java.lang.Object@9cab16 - java.lang.Object@9cab16

Tom Hawtin
 
R

Roedy Green

class HashClash {
public static void main(String[] args) {
final Object obj = new Object();
final int target = obj.hashCode();
Object clash;
long ct = 0;
do {
clash = new Object();
++ct;
} while (clash.hashCode() != target && ct<10L*1000*1000*1000L);
if (clash.hashCode() == target) {
System.out.println(ct+": "+obj+" - "+clash);
} else {
System.out.println("No clashes found");
}
}
}

think this works because obj is considered not living even before you
get into the loop, so its address/hashcode are up for grabs to
recycle.
 
T

Thomas Hawtin

Roedy said:
think this works because obj is considered not living even before you
get into the loop, so its address/hashcode are up for grabs to
recycle.

The obj reference is used in the println expression.

From a pedantic point of view, it could be removed, all except for a
copy of the hash code. One way to guarantee that objects are alive (from
1.5) is to later assign them to a volatile reference. Handy for
finalisers. Here is a corrected version of the program:

class HashClash {
private static volatile Object dummy;
public static void main(String[] args) {
final Object obj = new Object();
final int target = obj.hashCode();
Object clash;
long ct = 0;
do {
clash = new Object();
++ct;
} while (clash.hashCode() != target && ct<10L*1000*1000*1000L);
if (clash.hashCode() == target) {
System.out.println(ct+": "+obj+" - "+clash);
} else {
System.out.println("No clashes found");
}
dummy = obj;
dummy = clash;
}
}

Which gives:

62028120: java.lang.Object@9cab16 - java.lang.Object@9cab16

Tom Hawtin
 
C

Chris Uppal

Thomas said:
From 1.5 you can getter performance performance using
java.util.concurrent.atomic.AtomicInteger.

Do you happen to know what the performance difference is ? How much faster is
it than an acquire/release of an uncontended lock ? Or a contended lock ? How
well does the performance difference generalise across architectures ?

I could measure it on a uniprocessor Pentium box, but that's none too
representative these days, and anyway I want to do other things today...

-- chris
 
T

Thomas Hawtin

Chris said:
Thomas Hawtin wrote:




Do you happen to know what the performance difference is ? How much faster is
it than an acquire/release of an uncontended lock ? Or a contended lock ? How
well does the performance difference generalise across architectures ?

I could measure it on a uniprocessor Pentium box, but that's none too
representative these days, and anyway I want to do other things today...

I just tried to write a benchmark on my Celeron D box. The AtomicInteger
version got optimised away. It's essentially a wrapper around an
intrinsic. Presumably the really important benefits are on multithreaded
hardware where you want to avoid thread contention as much as possible.

Tom Hawtin
 
C

Chris Uppal

Thomas said:
I just tried to write a benchmark on my Celeron D box. The AtomicInteger
version got optimised away.

That should be fast enough for some applications ;-)

Presumably the really important benefits are on multithreaded
hardware where you want to avoid thread contention as much as possible.

And perhaps the AtomicInteger stuff can benefit by not having to force a full
resynchronisation of local/remote memory.

What I need is a loosely coupled multiprocessor box to test this on. Sadly, I
have neither the cash nor the space...

-- chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,796
Messages
2,569,645
Members
45,369
Latest member
Carmen32T6

Latest Threads

Top