Skip to content
Aleksandr Kuchuk edited this page May 22, 2016 · 3 revisions

About

Singleton is a design pattern which guarentee that we can have one(in some cases - not one) instance of our class. It's very useful in different situations, for example, database service, logger manager and etc.

For example:

public enum Car{
    DODGE, LAND_ROVER, AUDI
}

We can't create another instances of Car enum, and have only one specimen of each. So, a key feature of this template - the inability to create instances of the class, in addition provided.

Implementations of this patterns:

Implementations

Zero attempt

class Singleton {
private static Singleton instance = new Singleton();;
private Singleton() {}

public static Singleton getInstance() {
return instance;
}
}

Without lazy initialization and without handling exceptions.

Simple way, it's not good, but not bad. So-so.

First attempt

class Singleton {
private static Singleton instance;
private Singleton() {}

public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}

But it works fine only without multithreading. Why? Cause if we have more than one thread the first thread try to getInstance() of our Singleton, it see that instance is null and initialize it, but in the same time the second thread can call getInstance() and it see that instance is null too, cause first doesn't initialize instance yet. And second thread create another instance of Singleton() and it's bad, for example if creating new instance of our class is very expensive.

Bad for multithreading. It's not good.

Example

Second attempt

We add the synchronize for getInstance and have: public static synchronize Singleton getInstance()

Result:

class Singleton {
private static Singleton instance;
private Singleton() {}

public static synchronize Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}

or

public class Singleton{

    private static Singleton self = null;

    private SingletonImpl(){
        self = this;
    }

    public static synchronized Singleton getInstance(){
        return (self == null) ? new SingletonImpl() : self;
    }
}

And everything is good, except that we often use synchronization, but we want to synchronize our method once - only when we create instance. Synchronize - isn't very cost operation, but we lost our perfomance here, every time when we call getInstance() we have overheads, we create a critical section.

But it's good decision.

Example in code

Good.

Third attempt

public class DoubleCheckedLockingSingleton {
	private static volatile DoubleCheckedLockingSingleton instance;

	public static DoubleCheckedLockingSingleton getInstance() {
		DoubleCheckedLockingSingleton localInstance = instance;
		if (localInstance == null) {
			synchronized (DoubleCheckedLockingSingleton.class) {
				localInstance = instance;
				if (localInstance == null) {
					instance = localInstance = new DoubleCheckedLockingSingleton();
				}
			}
		}
		return localInstance;
	}
}

As we can see - we use double check. It's good decision, we remove problem with syncrhonize, but we use volitaile - and that's why this solution works only with Java > 1.5.

Why we use volitaile.

When we create our object, we:

  • allocate memory for object
  • initialize pointer
  • call object constructor

And on very-very low probability(in some JVMs for example) we can start to use our object before last point in list above. Thats why - use volatile, just in case.

It's good!

Example

Fourth attempt

public class BillPughSingleton {
	
	private BillPughSingleton() {
	}

	private static class SingletonHelper {
		private static final BillPughSingleton INSTANCE = new BillPughSingleton();
	}
	
	public static BillPughSingleton getInstance() {
		return SingletonHelper.INSTANCE;
	}
}

Lazy initialization, good perfomance, but we can't use it for non-static fields of class and it's hard to handle exceptions.

It's good decision.

Example

###Summary: To sum up:

  • Second and third examples - it's good for use!
  • Use final for Singleton by default and use inheritance for it in really necessary cases.
Clone this wiki locally