In JCIP we have a piece of code which looks like this:
Listing 4.2:
@ThreadSafe
public class PersonSet {
@GuardedBy("this")
private final Set<Person> mySet = new HashSet<Person>(); // line 3
public synchronized void addPerson(Person p) {
mySet.add(p);
}
public synchronized boolean containsPerson(Person p) {
return mySet.contains(p);
}
}
I was wondering if we change the third line to this:
private Set<Person> mySet = new HashSet<Person>(); // line 3, removes final
Is it true to say that the class is no longer thread-safe because the non-final variable mySet could have been null, even after the constructor exits and a reference to the PersonSet instance is published?
For example, is it true to say that a calling code like this may fail, or am I misunderstanding somthing? :
PersonSet p = new PersonSet();
SendToThreadB(p);
What if I have a restriction that does not allow the field to be marked “final” (like I may have to swap it with a new instance), what solutions are there to ensure that the class is still thread-safe without using final ?
If you remove the
final, instances become unsafe when used after unsafe publication. That is, if another thread gets access to the object without going throughsynchronized/volatileto produce an appropriate happens-before relationship then it may see a partially initialised object. In this case it would probably fail in a relatively safe way giving aNullPointerExceptionon dereferencing themySetfield. In theory, another thread could see the reference to theHashSet, but some or all of the fields of that object may not have been set.