Subject: What we can learn from Singleton Pattern -- Potential Concurrent Access Issues.
Author: Alex_Raj
In response to: What we can learn from Singleton Pattern -- There is always an alternative way.
Posted on: 12/05/2013 11:57:14 PM
Let's consider a real scenario: a sequence number or auto increment number is globally needed for all records or change events pushing into database. The number must be unique across all application environment. For this case, a sequence generator in singleton pattern is a perfect solution.
/**
* A Real Example: Sequence Generator -- (not right)
*/
public class SequenceGenerator
{
/* static field to ensure only one copy */
private static final SequenceGenerator INSTANCE = new SequenceGenerator();
private long sequenceNumber;
/* private constructor to prevent it from being instantiated from outside */
private SequenceGenerator() {
sequenceNumber = 0L; // or loading from persistence
}
/* public static method to ensure global point of access */
public static SequenceGenerator getInstance() {
return INSTANCE;
}
public long nextNumber() {
sequenceNumber++;
return sequenceNumber;
}
}
As singleton suggests only one instance and global accessibility, there is a very high chance that the instance methods are accessed concurrently due to the shared nature of the instance. Here in this example, the operation
sequenceNumber++; is not atomic and hence the method
nextNumber() must be mutual exclusively blocked.
/**
* A Real Example: Sequence Generator -- synchronized version
*/
public class SequenceGenerator
{
/* static field to ensure only one copy */
private static final SequenceGenerator INSTANCE = new SequenceGenerator();
private long sequenceNumber;
/* private constructor to prevent it from being instantiated from outside */
private SequenceGenerator() {
sequenceNumber = 0L; // or loading from persistence
}
/* public static method to ensure global point of access */
public static SequenceGenerator getInstance() {
return INSTANCE;
}
public synchronized long nextNumber() {
sequenceNumber++;
return sequenceNumber;
}
}
OR:
/**
* A Real Example: Sequence Generator -- atomic version
*/
import java.util.concurrent.atomic.AtomicLong;
public class SequenceGenerator
{
/* static field to ensure only one copy */
private static final SequenceGenerator INSTANCE = new SequenceGenerator();
private AtomicLong atomicSequenceNumber;
/* private constructor to prevent it from being instantiated from outside */
private SequenceGenerator() {
atomicSequenceNumber = new AtomicLong(0L); // or loading from persistence
}
/* public static method to ensure global point of access */
public static SequenceGenerator getInstance() {
return INSTANCE;
}
public long nextNumber() {
return atomicSequenceNumber.incrementAndGet();
}
}
>
> On 12/05/2013 11:49:13 PM
Alex_Raj wrote:
Early Instantiation Version:
If the program will always need an instance, or if the cost of creating the instance is not too large in terms of time/resources, the programmer can switch to eager initialization, which always creates an instance:
/**
* Early instantiation version - no synchronization is needed
*/
public class Singleton
{
/* static field to ensure only one copy */
private static final Singleton INSTANCE = new Singleton();
/* private constructor to prevent it from being instantiated from outside */
private Singleton() {
}
/* public static method to ensure global point of access */
public static Singleton getInstance() {
return INSTANCE;
}
public void sayHello() {
System.out.println("Hello");
}
}
This version has a number of advantages:
The instance is not constructed until the class is used.
There is no need to synchronize the getInstance() method, meaning all threads will see the same instance and no (expensive) locking is required.
The final keyword means that the instance cannot be redefined, ensuring that one (and only one) instance ever exists.
Lazy Inner Static Class Version:
/**
* Lazy inner static class version - no synchronization is needed
*/
public class Singleton
{
/* static class to ensure only one copy & lazy load as is needed*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
/* private constructor to prevent it from being instantiated from outside */
private Singleton() {
}
/* public static method to ensure global point of access */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
public void sayHello() {
System.out.println("Hello");
}
}
This version has a number of advantages:
The instance is not constructed until getInstance() method is used.
There is no need to synchronize the getInstance() method, meaning all threads will see the same instance and no (expensive) locking is required.
The final keyword means that the instance cannot be redefined, ensuring that one (and only one) instance ever exists.
References: