Deep Dive: Singleton Pattern
OK lets have a look at our first pattern. We will start with the ubiquitous and sometimes controversial singleton pattern. The singleton pattern is one of the most well known design patterns and is used extensively by frameworks such as Spring, pool mangers and loggers. Java EE offers an elegant and easy way to implement the singleton pattern. A singleton is an object that is only ever instantiated once within the life-cycle of the application. Once created it is not normally destroyed until the application terminates. If you only need one instance of a class and that instance needs to be accessible in different parts of the application such as an object for logging or database access then the singleton pattern provides a solution for this.
Conventional Implementation
[sourcecode language=”java” toolbar=”false”]
public class Logger {
private static Logger instance;
private Logger() {
// Creation code here
}
public static synchronized Logger getInstance() {
if(instance == null) {
instance = new Logger();
}
return instance;
}
}
Used like so:
Logger.getInstance();
[/sourcecode]
Here only one instance of the Logger is created. The first call to getInstance method creates the Logger singleton and stores it (instantiation is lazy). Subsequent calls to the getInstance method returns the previously created instance. The creation method is synchronized to ensure that only one instance is created (another thread cannot not come in and create another instance of the Logger while the first logger is being created). We have created a lazily instantiated thread safe singleton logger.
Implementation in Java EE.
[sourcecode language=”java” toolbar=”false”]
@Singleton
public class Logger {
private Logger(){
// Creation code here
}
}
Used like so:
@Inject
Logger logger;
[/sourcecode]
The first thing you will have noticed is how much less code is required to create a singleton. The EJB container creates an instance of the Logger class and will inject the same instance where ever it finds an injection point. Injection points are annotated @Inject. The creation of the singleton class is done by the container and the container knows to create only one instance because the class is annotated with the @Singleton stereotype annotation and concurrency is managed by the conclusion.
By default the singleton bean is initialized lazily, it wont be created until first use. Normally this is sufficient for most use case scenarios, however you may want the singleton bean to perform application start up tasks in which case you must annotate the bean with @Startup. The EJB container must ensure that the bean is initialized before it delivers client requests. Adding the @PostConstruct annotation to the method that performs application start up request.
[sourcecode language=”java” toolbar=”false”]
@Startup
@Singleton
public class Logger {
private Logger() {
// Creation code here
}
@PostConstruct
void startUpTask() {
// Perform Start Up Tasks
}
}
[/sourcecode]
Dependent Instantiation
The instantiation of your bean may depend on the initialisation of other beans. We can specify the bean on which we depend.
[sourcecode language=”java” toolbar=”false”]
@DependsOn("MyBean")
@Startup
@Singleton
public class Logger {
…
}
[/sourcecode]
Our singleton bean will not be instantiated until the successful instantiation of the MyBean has completed.
Conclusions so far:
Already we have seen how the Java EE implementation of the singleton pattern is markedly different to its classical implementation and requires substantially less code to achieve the same results. You have been given more control over when the bean is instantiated and what tasks it can perform enhancing its behavior.
Putting it all together we have the following code. This creates an early instantiated thread safe singleton logger bean that performs start-up tasks after the instantiation of the bean named MyBean.
[sourcecode language=”java” toolbar=”false”]
@DependsOn("MyBean")
@Startup
@Singleton
public class Logger{
private Logger(){
// Creation code here
}
@PostConstruct
void startUpTask(){
// Perform start up tasks
}
}
[/sourcecode]
Now lets look at how Java EE gives you even greater control over the pattern’s behaviour.
Java EE offers two types of concurrency management: container managed and bean managed concurrency. By default the container manages the concurrency, removing the need to implement the usual concurrency solution. However we are given the option to manage it ourselves by choosing bean managed concurrency. Add the annotation ConcurrencyManagementType.BEAN to the class definition.
[sourcecode language=”java” toolbar=”false”]
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Logger {
…
}
[/sourcecode]
We still need to be careful with method access as our bean is exposed to a concurrent environment. Two lock types control access to the beans business method: WRITE and READ.
Methods annotated WRITE, locks to other beans while it is being invoked. Methods that affect change will be annotated this way. Methods annotated READ allow concurrent access.
[sourcecode language=”java” toolbar=”false”]
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class Logger {
private Logger(){
// Creation code here
}
@Lock(LockType.WRITE)
public void addMessage(String message){
// Add message to log
}
@Lock(LockType.READ)
public String getMessage(){
// Get message
}
}
[/sourcecode]
A call to the getMessage method will be forced to wait unitl the addMessage method completes. This may result in a ConcurrentAccessTimeoutException if the addMessage() method does not complete within the specified timeout period. The timeout period can be configured with an annotation either at the class level or the method level.
[sourcecode language=”java” toolbar=”false”]
@AccessTimeout(value = 30, unit=TimeUnit.SECONDS)
@Lock(LockType.WRITE)
public void addMessage(String message){
// Add message to log
}
[/sourcecode]
Final conclusions:
As we have seen, Java EE has simplified the way we implement the singleton pattern. With one annotation we can implement a thread safe singleton with container managed concurrency. With further annotations we gain control over the way the singleton behaviours in a concurrent environment and how it acts when the application starts up.
Leave a Reply