-J2EE Developer at Turkiye Is Bankası Core Banking Exchange project -Java-Flex Developer in TAV IT. Built softwares for several international airport (istanbul, ankara, izmir, tblisi, batumi, emfidha) with JPA/Spring/CXF/BlazeDS on server side and Flex on rich clients. -JavaEE&Process Project Laader at HSBC. -Presenter at Eclipsist 2008, on Rapid Rich Internet Application Development. Murat has posted 8 posts at DZone. View Full User Profile

Design Patterns Revisited - Singletons, How Single They Are..

07.25.2008
| 12219 views |
  • submit to reddit

I decided to write several posts about design patterns. Whether you use them or not, they are important and make your life easier (and probably increase your salary). Even if you are really not interested in design patterns, you will face questions about them in interviews or architectural design meetings. So you better open up your brain and learn some - there's nothing wrong with impressing a few colleagues.

The first pattern I will post is the Singleton Pattern. The mighty gof explains this pattern as follows; "Ensure a class only has one instance, and provide a global point of access to it.".
Simple isn't it, if you need only one instance of a resource, mostly because it would be expensive to create and hold new instances, you make sure there is only one unique living instance of that object. Singleton objects are God and if you don't want chaos, conspiracy and fighting like Olympian Gods than you must make sure you have only one!

Going back to basics how do we construct and instantiate an object? Just by using the constructor. Even when we don't write one, the compiler creates a default one for us. So to take control from the compiler we just create a default non-argument constructor and mark it as private. So we ensure no one would ever has access to it, to create a new instance(A). Next we type a public static method so whoever wants to use our object must use that entry point to access(B).
    public class SingletonObj {

private final static Singleton instance = new Singleton(); // our unique instance

private Singleton() {} //A

public static Singleton getInstance() { //B

return instance;

}

}
This is the most simple way to show a Singleton. You may also want to use a static initializer so our "instance" will be initialized only when it is first accessed.
public class SingletonObj {

private final static Singleton instance = null;

static {

instance = new Singleton(); // our unique instance

}

private Singleton() {} //A
 Or you might check if it is initialized in getInstance method.
public class SingletonObj {

private final static Singleton instance = null;

private Singleton() {} //A

public static Singleton getInstance() { //B

if (instance == null){

instance = new Singleton(); // our unique instance

}

return instance;

}

}
All of the examples above work quite the same and ensure the current running JVM has only one instance. Most experienced developers are now quite ready to leave a comment on how to hack this design. Ok, you can't just really ensure, but it is a hack and you must be expecting your colleagues not trying to break your design and mess up with resources :)Since the JVM lets us access even the private methods by reflection and once we get the methods it is possible to change the access rules and create new instances. Here is how to create more instances from a singleton;
    public class SingletonHack {

public static void main(String [] args);

Class clazz = Class.forName("SingletonObj"); //we load the class

Constructor[] cons = clazz.getDeclaredConstructors(); //get the constructors

cons[0].setAccessible(true); //change access rights

SingletonObj noMoreSingleton = (SingletonObj) cons[0].newInstance();

//we have brand new instance

}
 
Actually this is not something new and I'm quite sure most of you already knew it. Java is just like having Jedi powers. "The dark side of the force is a pathway to many abilities some considered to be unnatural". When you really need, it offers a dark side to make things work but you shouldn't be using those powers and staying on the light side. If you feel you need such a hack you must look back and try to find what did you do wrong and put yourself in Anakin's shoes :) 
Published at DZone with permission of its author, Murat Yener.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Christoph Kutzinski replied on Wed, 2008/07/30 - 11:57am

Slava, did you test on a single or multicore computer? I got 12 sec unsynchronized vs. 22 sec synchronized on a 4 physical (8 logical) CPU Intel Xeon for 1 (not 10!) mio accesses. Quite a difference to a your results.

Also, you apparently are measuring the  time for thread creatio, too. And you are not considering the warm-up time for the VM (Hotspot-compiler).

Always be careful if you are trying to micro-benchmark something in Java. Most of the time you will actually measure something completely different from what you wanted to measure.

Christoph Kutzinski replied on Wed, 2008/07/30 - 12:17pm

Follow up:

- thread creation time is apparently neglible - got nearly the same numbers without it

- warm-up is probably unimportant, too, as the loop is executed many many times

- if incrementCounter() is unsynchronized, too, the test runs in 50 ms !! (and unsurprisingly counts wrong)

 

And now tell me again that that synchronization cost is negligible!

Slava Imeshev replied on Wed, 2008/07/30 - 12:26pm

With 0.2 microseconds per call it is negligible. If you had to optimze, scratching your had over it would be the last thing to do. 

Slava

 

Sebastian Jancke replied on Wed, 2008/07/30 - 1:36pm in response to: Slava Imeshev

Ok, you asked "where's context from", so I've answered ;-)

Indeed, it has it's cost: adding a DI framework, with configuration (embedded dsl or xml), right. But today's architecture mostly have an DI container running - or the context is so simple, that it is not needed. In that case, also the seperation of concerns on singletons may be ignored.

And  yes, most DI frameworks introduce a singleton to manage context(s) / configuration ;-)

 and a complicated object creating scheme just to avoid singleton seems odd.

 It's not about avoiding singleton, but it's about the bunch of problems, singletons introduce. In general applications need context (as can be seen in DI context, registries, ...), but that should be the only case, where singletoness is implemented by singletons.

-Sebastian

Slava Imeshev replied on Wed, 2008/07/30 - 1:47pm in response to: Sebastian Jancke

[quote=sebastian jancke]It's not about avoiding singleton, but it's about the bunch of problems, singletons introduce. [/quote]

 

Yes, what are they, the problems?

Slava

 

 

Tranquiliser Gt replied on Wed, 2008/07/30 - 9:11pm

Here is my test result with slight modification based on Slava's test case. Two threads were launched.

=============================

  Unsynchronized + Atomic Integer (counter)

=============================

time, ms: 796
counter: 20000000

=============================

  Synchronized

=============================

time, ms: 31469
counter: 20000000

===============================

  Violate + Double Checked Locking + Atomic Integer (counter)

===============================

time, ms: 938
counter: 20000000

Slava Imeshev replied on Wed, 2008/07/30 - 11:32pm

Guys, all I wanted was to show that the synchronization time is negligible enough for not having the dreaded double-checked lockinh. I bet there are ways to increment a counter wiked fast, but that wasn't the point.

 

Slava 

Christoph Kutzinski replied on Thu, 2008/07/31 - 2:22am in response to: Slava Imeshev

[quote=imeshev]

Guys, all I wanted was to show that the synchronization time is negligible enough for not having the dreaded double-checked lockinh.

[/quote]

Yes you wanted to show that. But we showed you that synchronization is not negligible in many cases (did you actually read my numbers?)

 [quote=imeshev]

I bet there are ways to increment a counter wiked fast, but that wasn't the point.

[/quote]

 It wasn't mypoint, either.

 

BTW: your advice is: "use synchronization". My advice would be: "use static initialization". Most (>90%) of the singletons I've seen in practice didn't need lazy initialization anyway, but still someone tried to apply some form of broken checked or double-checked locking on it.

What most people don't get, is that even static initialization is some form of lazy as it will only create the singleton instance the first time the singleton class is accessed. And that is usually the time when getInstance() is called the first time.

Slava Imeshev replied on Thu, 2008/07/31 - 3:03am in response to: Christoph Kutzinski

[quote=kutzi]

BTW: your advice is: "use synchronization". My advice would be: "use static initialization". Most (>90%) of the singletons I've seen in practice didn't need lazy initialization anyway, but still someone tried to apply some form of broken checked or double-checked locking on it.

[/quote]

 

If you roll back this pretty lengthy thread you will see that using a plain static initialization was the first option that I proposed, using the same arguments as yours. We agree in full agreement here.

Regards,

Slava Imeshev

 

Jan-kees Van Andel replied on Thu, 2008/09/04 - 7:49am in response to: Tranquiliser Gt

@tranquiliser: Btw, your singleton doesn't even compile, since a final static field can only be assigned a value @class initialisation time. This means directly, like this:

private static final Singleton instance = new Singleton();

 

or this, if you'd like to do some extra computations @startup:

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

 

And IF you decide to use double checked locking, be sure to make the field volatile AND only use it when you can guarantee that it will run on Java 5 or higher.

Jan-kees Van Andel replied on Thu, 2008/09/04 - 8:22am

Just a comment to all guys here who try to show the performance impact of synchronized.

Java is no C. While it is common to write a benchmark in C, most microbenchmarks are useless in Java. You'll have to take into account way too much (vendor specific) details of the underlying JVM/compiler/OS/hardware, making it almost impossible to write a useful benchmark.

Some things that influence your benchmarks:

  • The Java version (Java 6 for example, has many performance tweaks for synchronization),
  • Dynamic JIT compiling,
  • Warmup times,
  • Reordering, optimization or removal of useless code (And from a JVM perspective, test code is usually useless),
  • Garbage collection timing,
  • Your benchmark itself, since this is also a (concurrent) Java program that needs synchronization or garbage collection,
  • The OS/thread scheduler,
  • The architecture of your hardware,
  • and may others...

Those things can really cause misleading test results. A factor 10 to 100 on a whole test is not uncommon.

Expecially removing the useless code (dead code elision) and forgetting to do a correct warmup round are common mistakes when writing benchmarks.

So, while you may be tempted to measure performance using a benchmark, please don't do it. Or at least, don't take the results not too serious, unless you are a JVM expert.

If you need to know things about performance, look into the official documentation. Trying to compute it yourself is a waste of time (unless your name is Doug Lea :)).

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.