80 likes | 170 Views
Singleton Fun. Steven Seida. Multi-Threading Problem. public class Foo { private Foo myInstance; private Foo() {} … public static Foo getInstance() { if (myInstance==null) { myInstance = new Foo(); } } } //class foo. Result is Multiple Instances. Time Step --1-- --2--
E N D
Singleton Fun Steven Seida
Multi-Threading Problem • public class Foo { • private Foo myInstance; • private Foo() {} • … • public static Foo getInstance() { • if (myInstance==null) { • myInstance = new Foo(); • } • } • } //class foo Result is Multiple Instances Time Step --1-- --2-- --3-- --4-- --5-- Process #2 public static Foo getInstance() { if (myInstance==null) { myInstance = new Foo(); } Process #1 public static Foo getInstance() { if (myInstance==null) { myInstance = new Foo(); }
Simple Fix – Synchronized Method No two threads may enter the method at the same time. • public class Foo { • private Foo myInstance; • private Foo() {} • … • public static synchronized Foo getInstance() { • if (myInstance==null) { • myInstance = new Foo(); • } • } • … • } //class foo • Causes blocking around any calls to getInstance() • Works but may be more costly than we can afford.
Improve Multi-Threading Option 1 • Need to lock on creation of the variable, rather than an entire method. • public class Foo { • private static Foo myInstance = new Foo(); • private Foo() {} • … • public static Foo getInstance() { • myInstance = new Foo(); • } • … • } //class foo Greedy initialization is guaranteed to be thread safe.
Improve Multi-Threading Option 2 • Need to lock on creation of the variable, rather than an entire method. • public class Foo { • private volatile static Foo myInstance; • private Foo() {} • … • public static Foo getInstance() { • if (myInstance == null) { • synchronized (Foo.class) { • if (myInstance == null) { • myInstance = new Foo(); • } • } • } • return myInstance; • } • … • } //class foo Never cached thread-locally – always writes/read with ‘main memory’. Only synchronize the first time through. Confirm someone else didn’t beat you here.
Volatile and Synchronized* • Declaring a volatile Java variable means: • The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory"; • Access to the variable acts as though it is enclosed in a synchronized block, synchronized on itself. • We say "acts as though" in the second point, because to the programmer at least (and probably in most JVM implementations) there is no actual lock object involved. *http://www.javamex.com/tutorials/synchronization_volatile.shtml# Applicable to Java 5 and later
Synchronized vs Volatile • In other words, the main differences between synchronized and volatile are: • a primitive variable may be declared volatile (whereas you can't synchronize on a primitive with synchronized); • an access to a volatile variable never has the potential to block: we're only ever doing a simple read or write, so unlike a synchronized block we will never hold on to any lock; • because accessing a volatile variable never holds a lock, it is not suitable for cases where we want to read-update-write as an atomic operation (unless we're prepared to "miss an update"); • a volatile variable that is an object reference may be null (because you're effectively synchronizing on the reference, not the actual object). • Note: Attempting to synchronize on a null object will throw a NullPointerException.