The one and only Singleton pattern

In this post, I will talk about how to ensure only 1 instance of a class is created using the Singleton pattern.

See code at Github repo (https://github.com/yangju2011/design-patterns). Code is adapted from Head First Design Patterns: A Brain-Friendly Guide 1st Edition by Eric Freeman and Elisabeth Robson.

Definition

Ensures a class has only one instance and provides a global point of access to it.

Classic Singleton

See Singleton.java. We have a private constructor for Singleton() and a public method getInstance to get the uniqueInstance. Note that the Singleton object is only created once.

Output:

..Call getInstance
Creating a new Singleton object
..Call getInstance

..Check if Singleton is unique
Singleton objects are the same

Budget Pool

See BudgetPool.java. In this example, we have a Singleton object BudgetPool which contains a private variable budget and several public methods to update budget.

When we run BudgetPoolTest, we get

Creating a new BudgetPool object
...

No budget left, cannot use
Current budget: 0
...

No budget left, cannot reset
Current budget: 0
...

Refill budget to 10
Current budget: 10
...

Use budget of 1
Current budget: 9
...

Still have budget left, not refill
Current budget: 9
...

Reset budget
Current budget: 0

The BudgetPool class is not thread safe. We have multiple threading in BudgetPoolThreadTest.java. In the first few threads, the instance is not initialized. Multiple threads can enter the if-else loop and create multiple instances.

When we run BudgetPoolThreadTest, we may get the following output. Note that we have two Creating a new BudgetPool object instead of one.

Current thread : Thread-0
Current thread : Thread-1
Thread-0
Thread-1
Creating a new BudgetPool object
...
Thread-1
Creating a new BudgetPool object
...
Thread-0
Refill budget to 10
Refill budget to 10
Current budget: 10
...
Current budget: 10
...
Thread-0
Thread-1
Use budget of 1
Use budget of 1
Current budget: 9
Current budget: 9

Thread-safe Singleton

1. synchronized method

If calling getInstance does not cause substantial overhead for your application, we can synchronize the method directly, as in BudgetPool.

When we use a synchronized block, internally Java uses a monitor also known as monitor lock or intrinsic lock, to provide synchronization. These monitors are bound to an object, thus all synchronized blocks of the same object can have only one thread executing them at the same time. See reference.

Synchronizing a method can decrease performance by a factor of 100!!!

The output shows that we only create the instance once.

Current thread : Thread-0
Current thread : Thread-1
Thread-0
Creating a new BudgetPool object
...
Thread-0
Thread-1
...
Thread-1
Refill budget to 10
Refill budget to 10
Current budget: 10
...
Thread-0
Use budget of 1
Current budget: 10
...
Thread-1
Use budget of 1
Current budget: 9
Current budget: 8

2. eagerly created instance

We can create the Singleton object eagerly as in BudgetPool with a static initializer. Using this approach, we rely on the JVM to create the unique instance when the class is loaded. The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable. This may get messy due to the way static initialization handled by Java.

3. double-checked locking

In BudgetPool, we use double-checked locking to

  • first check if an instance is created
  • if not, THEN we¬†synchronize. We only synchronize the first time.

The volatile keyword ensures that multiple threads handle the uniqueInstance correctly when it is being initialized.

The Java¬†volatile¬†keyword marks a Java variable as being stored in¬†main memory. That means, that every¬†read¬†of a volatile variable will be read from the computer’s main memory (RAM), and not from the CPU cache and that each¬†writes¬†to a volatile variable will be written to the RAM, and not just to the CPU cache. The Java volatile keyword guarantees¬†visibility¬†of changes to variables across threads. See¬†reference.

Global variable v.s. Singleton

Global variables in Java are static references to objects. This may get messy due to the way static initialization handled by Java. A global variable can provide a global access to a class like Singleton, but cannot ensure only one instance of the class exists.

AtomicReference v.s. Singleton

AtomicReference Java class provides operations on underlying object reference that can be read and written atomically. It has get and set methods that work like reads and writes on volatile variables to make class thread safe.

AtomicReference allows you to atomically change their reference to the object — and in the case of a singleton there should be one instance only and no one should be able to change that ever again for the execution of your app.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.