Sharing Objects Safely.
Table of Contents
Safe Object Sharing
Initialization Safety Guarantee
Problem: Clients may get a reference to an uninitialized object!
final class Account {
private int balance;
public Account(int balance) { this.balance = balance; }
public String toString() { return "" + balance; }
}
class Company {
private Account account = null;
public Account getAccount() { // lazy initialization
if(account == null) account = new Account(10000);
return account;
}
}
Two different threads doing getAccount may get different balances. The address may already be availble before the init of the object is done.
Solution 1:
class Company {
private volatile Account account = null;
public Account getAccount() {
if(account == null) {
account = new Account(10000);
}
return account;
}
}
- Happens-before relation guarantees that fields set in the Integer- constructor are visible (as the invocation of constructor happens-before the assignment to the volatile field account)
- Remark: no singleton guarantee (assumption: not required)
Even better solution:
class Account {
private final int balance;
public Account(int balance) { this.balance = balance; }
public String toString() { return "" + balance; }
}
- JMM guarantees that final fields are only visible after they have been initialized!
Guarantees of the JMM
- Final fields (of primitive type and references) are visible after initialization.
- The initial (final) values are always visible, not the default (0, null, …) values
- This guarantee only holds for the final fields!
- For final references, the JMM guarantees that all referenced objects are visible after initialization if accessed over the final reference.
- Consequences
- At the end of the initialization phase of an object with final fields, all final fields (and its transitive hull) are flushed into main memory
- i.e. this data becomes visible BEFORE the address of the object becomes visible
- Useful for immutable objects Advise: declare fields in immutables as final (for initialization guarantee)
Requirement: Safe construction
Initialization-Safety is only guaranteed if an object is accessed after it is fully constructed.
Common mistake is to allow the this reference to escape in the constructor (listeners for example). To avoid this, use a factory pattern.
Final vs. Volatile
final
- Only at the end of the constructor a (partial) flush happens
- Only the first access leads to a (partial) refresh
- After first access no refresh is performed (final fields cannot change)
- Changes in referenced objects do not become visible automatically
volatile
- Each read access guarantees that the most recent data is seen
- No guarantees for referenced objects (beyond happens-before guarantees)
Basically we use final for read only and volatile for changing variables.