There was a fair bit of disagreement on this topic (although it
appears we have agreement on everything else, so that isn't too bad).
Everyone agrees that there must be a way to construct a class
that is guaranteed to be atomic and immutable, so that data races
cannot allow someone to see an instance change.
==== The "DEC position"
The Dec people argued that data races are bugs, that the others
guarantees should be sufficient to guarantee VM safety, and that
should be written to guarantee API safety. For example,
java.lang.String should be made truly immutable by either
making all methods synchronized, or put calls to Thread.memoryBarrier()
where needed (either in the String methods, or in the methods that
take Strings and can't be allowed to see Strings change).
==== The "Sun position"
The Sun people argued that unless they saw convincing numbers that
it would be expensive for real programs on real machines, they want
full blown initialization safety for all fields. Furthermore, they
say that if we can guarantee this, then the existing single and double
check idioms should become approved and recommended idioms.
If they are convinced that this would be too expensive, then they want some
other _efficient_ way of making a class truly immutable; making all
of the methods synchronized, or putting calls to
Thread.memoryBarrier() are unacceptable. One solution would be to
attach special semantics to final; one issue with this is that it
would mean that marking a field as final may have the result of
making your program run slower on SMPs with a weak memory model, a
==== My comments on the Dec view:
Making a class immutable by making all of the methods
synchronized or by adding explicit memory barriers is far too heavy
weight and expensive to be acceptable:
* It is only required for a very small % of Java platforms (shared
memory multiprocessors with weak memory models)
* It will be expensive, even on systems where it isn't required for
* It will be harder to do compiler transformations to remove or reduce
the cost of general synchronization or memory barriers than
transformations to remove or reduce the cost of synchronization to
guarantee true immutability.
* If you tell people that they have to use a particular coding style
that will result in slower execution on all processors, in order
to preclude the theoretical (but unlikely) possibility of something
bad happening on 0.01% of Java platforms, the result will be that no one
uses that style.
* One possibility is that people develop different versions of their
libraries: one for execution on almost all machines, and another version
that will run correctly on shared memory multiprocessors with weak
memory models. This directly contradicts the WORA philosophy, would be
a maintenance nightmare, and just isn't going to happen.
=> Whatever is done to guarantee initialization safety must turn into
a no-op with no performance cost on machines where it isn't needed.
The synchronization and memory barriers don't cut it.
==== My comments on the Sun view:
I truly hate and detest the idea that we will tell programmers
that some data races are an acceptable and recommended programming
* We could hope to train programmers that "Data races are evil". I
don't think we could ever get more than a handful of programmers to
correctly understand that some kinds of data races are good, and others
kinds of data races are evil, unless we made it very simple (e.g.,
data races on immutable objects are OK).
* There are tools that perform static and dynamic checks for data races.
I have used some of them, and think these tools are an essential part of
the development of any reliable, heavily multi-threaded program. I don't
think it will be possible to teach these tools the distinction between
good vs. evil data races (once again, unless it is something very
simple, like "OK to race on immutable objects").
* If data races were officially sanctioned, but you wanted to develop
your own code free of data races, it would be hard to use data race
detection tools unless data races are removed from the JRE.
* It is unpleasant that constructors have a special magic power for
synchronization that can't be obtained any other way.
* Data races rot the brain and corrupt the soul.
Also, I suspect that guaranteeing full initialization safety will prove
to have a substantial performance impact, more than is acceptable. I
agree that something stronger than a suspicion is needed, but I worry
that focusing on "existing machines" may be too restrictive; for
example, do SMP Merced systems even exist at the moment? If it isn't
a problem on today's machines, but it becomes a serious problem on
machines 5 years from now, we won't be able to change this decision
because the installed code base will be huge and we will have trained
a generation of Java programmers to use data races.
==== Alternatives I'll toss out for comment:
Choice 1: guarantee initialization safety only for final fields. I think that
this won't be too expensive to discourage use of final, and then only
on very few machines.
Choice 2: Use a marker interface SecureImmutable; implementing this interface
means that your class and all of your fields must be final, that any
reference fields must be to arrays (that you don't modify) or to
SecureImmutable objects, the VM will guarantee initialization safety,
the compiler may warn against things like returning a reference to an
array or accepting a reference to an array in a constructor, and
Serialization might do special things to prevent creation of aliases
to your internal representation.
Rational: if your security depends on a class being truly immutable, there
are lots of things you need to worry about, and memory model issues are
the least of them. Why not have a special way of doing all of these?
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel
This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:18 EDT