January 15, 2010 Design Patterns
January 15, 2010 Design Patterns
Imagine that you need some global logging system in your application.
You need to be able log your messages to some file at any point of your application, but also you need to numerate your messages.
How can you accomplish this?
Class which is represented below is example of Singleton Desing Pattern.
private LoggerSingleton(){}
private int _logCount = 0;
private static LoggerSingleton _loggerSingletonInstance = new LoggerSingleton();
public static LoggerSingleton GetInstance(){
return _loggerSingletonInstance;
}
public void Log(String message){
System.out.println(_logCount + “: “ + message);
_logCount++;
}
}
You are going to start execute your doHardWork method and you want to log that you just started doing hard work and what occured doing it.
So your functional system could look like below:
logger.Log(“Hard work started…”);
processor.processTo(5);
logger.Log(“Hard work finished…”);
}
As you see there is some class called HardProcessor. We should not really care how it works. All about we should care is that when it is created we want to log this and also we want to log when it is done with some calculations.
public HardProcessor(int start){
_start = start;
LoggerSingleton.GetInstance().Log(“Processor just created.”);
}
public int processTo(int end){
int sum = 0;
for(int i = _start; i <= end; ++i){
sum += i;
}
LoggerSingleton.GetInstance().Log(“Processor just calculated some value: “ + sum);
return sum;
}
}
Here is output of your programm:
I wrote this example when was in train with my friend. And I showed him my story and he said that I wrote Monostate. -“Me? Where?”. We took a look at my code and the only one variable I was using _logCount was static initially.
What is the difference between Singleton and Monostate?
Singleton is way to ensure that class has only one instance. Monostage if see globally on it do the same as GOF Singleton. All variables of it are static and this allows you to have many instances of monostate, but of course they will share the same data.
This also resolves many issues with multiconcurrecy usage of Singleton.
Lets take a look at my example once again. Could be that your logger is no so trivial, maybe on start of application you want to open some log file and get latest message number, so logger will continue to write to file with correct numbers.
Also your application is not so trivial. Maybe you are accessing your Singleton not only from just different places in your code but from different threads.
But since your constructor interacts with IO it takes time to construct your instance. So GetInstance() in reality in such situation could create TWO instances. Not really good for your application.
For our luck there are many ways to resolve this issue. Commonly we use Double-Checked Locking.
private ThreadSafeLoggerSingleton() {
//reads data from some file and gets latest number of message
//_logCount = got value here…
}
private int _logCount = 0;
private static ThreadSafeLoggerSingleton _loggerInstance;
public static ThreadSafeLoggerSingleton GetInstance(){
if (_loggerInstance == null) {
synchronized (ThreadSafeLoggerSingleton.class) {
if (_loggerInstance == null)
_loggerInstance = new ThreadSafeLoggerSingleton();
}
}
return _loggerInstance;
}
public void Log(String message) {
System.out.println(_logCount + “: “ + message);
_logCount++;
}
}
The way is not single and not all implementations of the DCL work! Read this good article on this: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Also as you see in method Log I’m using variable of my class and do operation on it, which is almost atomary. But if operations will be more complex you will need synchronization there also.
Go to: My Design Patterns Table
Markdown | Result |
---|---|
*text* | text |
**text** | text |
***text*** | text |
`code` | code |
~~~ more code ~~~~ |
more code |
[Link](https://www.example.com) | Link |
* Listitem |
|
> Quote | Quote |
That article is illustrative and useful. It reminded me DCL and some problems.
Optimization of such code isn't only JVM problem, but it is common problem of compilers, with optimization support. 'cause replace duplicate condition with one boolean variable is the simplest way of optimization.
As far as this problem is so common in multi-thread applications, it was fixed in recent versions of JVM.
There is "volatile" keyword, in modern version of Java. It forbids any optimization of variable declaration, assignation and suppresses optimization of any expression, which contains this variable.
To make your code working sure properly, you should change it like this:
private static volatile ThreadSafeLoggerSingleton _loggerInstance;
public static ThreadSafeLoggerSingleton GetInstance(){
if (_loggerInstance == null) {
synchronized (ThreadSafeLoggerSingleton.class) {
if (_loggerInstance == null)
{
synchronized (ThreadSafeLoggerSingleton.class) {
_loggerInstance = new ThreadSafeLoggerSingleton();
}
}
}
}
return _loggerInstance;
}
As I catch from your comment having one Boolean variable like "initialized" should be also enough to resolve this issue. Correct?
Ah, I gotcha you are saying there that compiler will optimize verification with replacing it with one boolean variable and that will not work. Cool, thanks.