Well, the concept is reading the JLS and understanding it. Chapter 17 of the JLS "Threads and Locks" describes memory visibility and synchronization. Section 17.5 "Final Field Semantics" describes the memory visibility semantics for final fields. That section says in part:
final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.
The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.
So you need to:
- Make
address
both final and private.
- For any mutable object, you must prevent the reference to that object from being seen externally.
In this case, #2 probably means you can't return a reference to Address like you have with getAddress()
. And you have to make a defensive copy in the constructor. I.e., make a copy of any mutable parameter, and store the copy in Employee. If you can't make a defensive copy, there's really no way to make Employee immutable.
public final class Employee{
private final int id;
private final Address address;
public Employee(int id, Address address)
{
this.id = id;
this.address=new Address(); // defensive copy
this.address.setStreet( address.getStreet() );
}
public int getId(){
return id;
}
public Address getAddress() {
Address nuAdd = new Address(); // must copy here too
nuAdd.setStreet( address.getStreet() );
return nuAdd;
}
Implementing clone()
or something similar (a copy ctor) would make creating defensive objects easier for complicated classes. However, the best recommendation I think would be to make Address
immutable. Once you do that you can freely pass around its reference without any thread-safety issues.
In this example, notice I do NOT have to copy the value of street
. Street
is a String, and strings are immutable. If street
consisted of mutable fields (integer street number for example) then I would have to make a copy of street
also, and so on ad infinitum. This is why immutable objects are so valuable, they break the "infinite copy" chain.
Since this question is getting popular, I should also add a mention of Brian Goetz's book, Java Concurrency in Practice, which is how I learned about these techniques, and I'm basically paraphrasing that book above.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…