Click here to Skip to main content
15,881,967 members
Articles / Programming Languages / Java

In Search of a Perfect Singleton

Rate me:
Please Sign up or sign in to vote.
2.94/5 (12 votes)
14 Sep 2006CPOL5 min read 112.8K   18   42
This article is a just an introspection into how to come up with the perfect singleton. The article tries to explore based on real time experience & explain in the common developer's perspective. The current thoughts hold good only for single-threaded applications.
“Design and programming are human activities; forget that and all is lost.” - Bjarne Stroustrup

Singleton Pattern

Well, to begin with, Singleton is one of the most commonly used - as well as misused - design patterns. Why did I say misused? You will get the answer as you read on.

Singleton Pattern is usually used to restrict the instances of a class, that is the objects within the life cycle of the software program to one. But why do we need to use Singletons? We use them:

  • to coordinate actions across the system
  • to improve upon the efficiency of the system

Care should be taken to identify the need & benefits of using a singleton. Often, we tend to use singletons to hide a global object.

How to Create a Singleton?

Most of us know how to create a singleton. We have done it so many times by now, isn't it? But let us just do a quick recap.

The thumb rule for singletons is that the direct instantiation through the constructor should be prevented. Then how do we instantiate or access the object? The solution is to provide a method that creates a new instance of the object if one does not exist. If an instance already exists, it simply returns a reference to that object.

C++
C++
class Singleton {
	Singleton() {
	}
};

Remember the default access in a C++ class is private. But in case of Java, constructor inherits the access modifier of the class. If no access modifier is defined on the class, then the default constructor has the default access implied by no access modifier. (Reference: Java Language Specification Second Edition 8.8.7 Default Constructor).

This means that in case of Java, care should be taken to explicitly make the access modifier of the constructor as private.

Java
Java
package ...;
public class Singleton { 
	private Singleton() { 
	} 
};

Now that we have prevented the direct instantiation of the class, we need to provide a mechanism to access the object. Here is how we do this:

C++
C++
class Singleton { 
public: 
	static Singleton* getInstance() { 
		if (NULL == _instance) { 
			_instance = new Singleton(); 
		} 
		return _instance; 
	}

private: 
	// default constructor  
	Singleton() { 
	}

	// the single instance of the object 
	static Singleton* _instance; 
};

Singleton* Singleton::_instance = NULL;
Java
Java
public class Singleton {
	public static Singleton getInstance() { 
		if (null == _instance) { 
			_instance = new Singleton(); 
		} 
		return _instance; 
	}

	// default constructor
	private Singleton() {
	}

	// the single instance of the object
	private static Singleton _instance; 
};

Now we have ensured that only one instance exists and gives access to the same. Now other required functionality can be added to the Singleton class.

Is That All?

Are we done? No, not quite exactly. Remember I had said that this is an attempt to create a perfect Singleton. So what is wrong with the above? Have we missed something? Yes, we did. Though we have prevented the direct instantiation, an object can still be created. If you recollect the basics, all C++ classes, apart from having their default constructors also have:

  • copy constructor
  • assignment operator

We need to declare these also as private so as to prevent the access. You may ask why. We do so because:

  • a copy constructor will create a new object from the existing one. But we are interested in limiting the instance to only one.
  • an assignment operator is not required for a Singleton; ‘_instance’ is the only data member & has to point to the same object across all instances.
C++
C++
class Singleton {
public:
	static Singleton* getInstance() {
		if (NULL == _instance) {
			_instance = new Singleton(); 
		} 
		return _instance; 
	}

private: 
	// default constructor 
	Singleton() {
	}

	// copy constructor 
	Singleton(const Singleton&) { 
	}

	// assignment operator 
	Singleton& operator=(const Singleton&) {
		return *this; 
	}

	// the single instance of the object 
	static Singleton* _instance; 
};
 
Singleton* Singleton::_instance = NULL;

In case of Java, we tend to forget about cloning just as we forget about copy constructor. Though our Singleton class does not define a clone method, we need to do so because java.lang.Object class from which our Singleton is inherited from defines the clone() method. So to make our Singleton foolproof, we need to add clone() method & prevent access to the same. Since we cannot make it private, we can either make it protected or override it & throw an exception or both.

Java
Java
public class Singleton {
	public static Singleton getInstance() { 
		if (null == _instance) { 
			_instance = new Singleton(); 
		} 
		return _instance; 
	}

	// default constructor 
	private Singleton() { 
	}

	// clone 
	protected Object clone() throws CloneNotSupportedException { 
		throw new CloneNotSupportedException(); 
	} 

	// the single instance of the object private 
	static Singleton _instance; 
};

How Do We Release the Singleton?

Now we have prevented alternatives to create another instance of the class. But what about the destruction of the instance? Can we define a destructor & delete _instance in it? This though looks very simple enough, it isn’t. If you do the above, you will have your code as:

C++
C++
class Singleton {
public: 
. . . 
. . . 
	~Singleton() { 
		delete _instance; 
	} 
};

What actually happens in that destructor, is that delete _instance invokes the destructor again & puts it into an infinite loop. So the easy way out seems to be to depend on the post program termination clean-up to release the memory. But then it is not a good practice to depend on the post program termination clean up for the new that we had done. Hence I recommend to have a releaseInstance() and to prevent access to the default destructor by making it private.

C++
C++
class Singleton { 
public: 
	static Singleton* getInstance() {
		if (NULL == _instance) { 
			_instance = new Singleton(); 
		} 
		return _instance; 
	}
 
	static void releaseInstance() { 
		if (NULL != _instance) { 
			delete _instance; 
			_instance = NULL; 
		} 
	}
 
private: 
	// default constructor 
	Singleton() { 
	}
 
	// default destructor 
	~Singleton() { 
	}
 
	// copy constructor 
	Singleton(const Singleton&) { 
	}
 
	// assignment operator 
	Singleton& operator=(const Singleton&) { 
		return *this; 
	}
 
	// the single instance of the object 
	static Singleton* _instance; 
};
 
Singleton* Singleton::_instance = NULL;

Tune the Release of Singleton

Now that looks perfect. All said & done, there still is a catch. When should releaseInstance() be called? Ideally, this should be called when the application exits. But in a real time situation when there are multiple developers working on the application code, it just remains a mirage. So what do we do? We will use a reference count to ensure that the actual destruction of the Singleton happens only when it is not being referenced anymore. So we add a static reference count to the Singleton.

C++
C++
class Singleton { 
public: 
	static Singleton* getInstance() { 
		if (NULL == _instance) { 
			_instance = new Singleton(); 
		} 
		_referenceCount++; 
		return _instance; 
	}
 
	static void releaseInstance() { 
		_referenceCount--; 
		if ((0 == _referenceCount) && (NULL != _instance)) { 
			delete _instance; 
			_instance = NULL; 
		} 
	}
 
private: 
	// default constructor 
	Singleton() { 
	}
 
	// default destructor 
	~Singleton() { 
	}
 
	// copy constructor
	Singleton(const Singleton&) {
	}
 
	// assignment operator
	Singleton& operator=(const Singleton&) { 
		return *this; 
	}
 
	// the single instance of the object 
	static Singleton* _instance;
 
	// the count of references 
	static int _referenceCount; 
};
 
int Singleton::_referenceCount = 0; 
Singleton* Singleton::_instance = NULL;

This is much better, but still not completely foolproof. If the releaseInstance() is called more than required by one particular user, it will lead to a situation where an instance is deleted even though it is actually still very much in use. On the contrary, if the releaseInstance() is not called by a user, then the Singleton will never be deleted & we are back to depending on the post program termination clean up.

In case of Java, as far as my knowledge goes, garbage collection takes care of releasing the instance when it is not referenced anymore. So not much problem as envisaged above for C++.

Finale

A simple way to handle the limitations in C++ is through effective code review. But the risk still remains as it is directly proportionate to the effectiveness of the code review. Though there is another workaround to minimize this risk, there always seems to be a way to hack through.

I am working on a way to perfect the implementation of this common pattern.

Note

The above holds good only for single-threaded applications. I will updated the article for multi-threaded applications shortly. The code was written using Eclipse 3.2 with CDT.

History

  • 14th September, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Japan Japan
The Saint is a software professional having worked extensively on UI development on VC++ in the intial years.

He currently works as a System Analyst catering to project development & management. He loves to explore & improve his all round knowledge & technical skills.

He is addicted to playing computer games, loves to travel & listen to music.

Comments and Discussions

 
GeneralMy vote of 4 Pin
califf2226-Aug-13 6:55
califf2226-Aug-13 6:55 
Questionhow to prevent throw to copy the singleton ? Pin
t_deak2-Nov-07 23:46
t_deak2-Nov-07 23:46 
GeneralPerfect Singleton deconstruction Pin
coolman545311-Jan-07 6:07
coolman545311-Jan-07 6:07 
GeneralAlternative Pin
User 34290117-Oct-06 9:09
User 34290117-Oct-06 9:09 
GeneralRe: Alternative Pin
t_deak3-Nov-07 0:11
t_deak3-Nov-07 0:11 
QuestionHave you ever read Alexandrescu books??? [modified] Pin
NickViz18-Sep-06 21:09
NickViz18-Sep-06 21:09 
AnswerAnd..... Pin
rm82219-Sep-06 3:13
rm82219-Sep-06 3:13 
GeneralRe: And..... Pin
Anonymuos20-Sep-06 11:50
Anonymuos20-Sep-06 11:50 
GeneralRe: And..... Pin
NickViz2-Oct-06 2:08
NickViz2-Oct-06 2:08 
Generalautomatic destruction Pin
maurycy widera18-Sep-06 9:16
maurycy widera18-Sep-06 9:16 
GeneralSingleton == Global Variable == Bad Design Pin
Anonymuos15-Sep-06 8:46
Anonymuos15-Sep-06 8:46 
GeneralRe: Singleton != Global Variable Pin
Cleavitt7615-Sep-06 10:33
Cleavitt7615-Sep-06 10:33 
GeneralRe: Singleton != Global Variable Pin
Anonymuos17-Sep-06 1:39
Anonymuos17-Sep-06 1:39 
GeneralRe: Singleton != Global Variable Pin
Cleavitt7618-Sep-06 5:07
Cleavitt7618-Sep-06 5:07 
GeneralRe: Singleton != Global Variable Pin
Zac Howland18-Sep-06 7:06
Zac Howland18-Sep-06 7:06 
GeneralRe: Singleton != Global Variable Pin
EhsanShal27-May-07 17:14
EhsanShal27-May-07 17:14 
GeneralRe: Singleton != Global Variable Pin
Zac Howland29-May-07 6:43
Zac Howland29-May-07 6:43 
I'm assuming that you meant to reply to the OP? I don't claim singletons are global variables ...

That said, I disagree with one concept in your blog: Singletons are single. If you want a controlled number of instances of an object, you are dealing with an object pool, not a singleton.

If you decide to become a software engineer, you are signing up to have a 1/2" piece of silicon tell you exactly how stupid you really are for 8 hours a day, 5 days a week

Zac

AnswerRe: Singleton != Global Variable Pin
EhsanShal30-May-07 17:15
EhsanShal30-May-07 17:15 
GeneralRe: Singleton != Global Variable Pin
Zac Howland31-May-07 5:38
Zac Howland31-May-07 5:38 
GeneralImproving the reference counting ... Pin
lemur214-Sep-06 23:10
lemur214-Sep-06 23:10 
GeneralRe: Improving the reference counting ... Pin
Zac Howland18-Sep-06 7:08
Zac Howland18-Sep-06 7:08 
GeneralAnother way to (not) deal with reference counting Pin
Laurent RICHARD18-Sep-06 10:35
Laurent RICHARD18-Sep-06 10:35 
GeneralC# Perfect Thread Safe Singleton Pin
Alois Kraus14-Sep-06 22:52
Alois Kraus14-Sep-06 22:52 
QuestionWhat about thread safety? Pin
Anders Dalvander14-Sep-06 20:37
Anders Dalvander14-Sep-06 20:37 
AnswerRe: What about thread safety? Pin
The.Saint14-Sep-06 20:49
The.Saint14-Sep-06 20:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.