Singleton, DCL, Holder Idiom, and When Singleton is not a Singleton

For eager-initialization singleton, the simplest way is to use an enum type with only 1 INSTANCE.
public enum EnumSingleton implements Calc {
INSTANCE;

public int add(int a, int b) {
return a + b;
}
}
A traditional, non-enum implementation of eager-initialization singleton is:
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();

private EagerSingleton() {}

public static EagerSingleton getInstance() { return instance; }
}
A lazy-initialization singleton implemented with double-checked locking (DCL), which works in Java 5 onward:
package test.concurrent;
public final class MySingleton1 {
private static volatile MySingleton1 instance;

private MySingleton1() {}

public static MySingleton1 getInstance() {
MySingleton1 result = instance;
if(result == null) {
synchronized (MySingleton1.class) {
result = instance;
if(result == null) {
instance = result = new MySingleton1();
}
}
}
return result;
}
A lazy-initialization singleton implemented with holder idoim (applicable to lazy static field):
package test.concurrent;
public final class MySingleton2 {
private MySingleton2() {}

private static class Holder {
private static final MySingleton2 instance = new MySingleton2();
}

public static MySingleton2 getInstance() {
return Holder.instance;
}
A main method that spawns multiple threads calling getInstance():
public static void main(String[] args) {
int numOfThreads = Integer.parseInt(args[0]);
for(int i = 0; i < numOfThreads; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println(MySingleton1.getInstance());
}
});
t.start();
}
}
When the same singleton class is loaded by different class loaders, each loader has its own class data. MySingleton1.class loaded by classloader1 is a distinct class from MySingleton1.class loaded by classloader2. Hence multiple instances will be created when calling MySingleton1.getInstance().

To test this scenario, write a servlet that calls MySingleton1.getInstance, and print it. Package the servlet class and MySingleton1 class in test.war, deploy it to appserver. Redeploy the same war in a different context root test2. Invoke the 2 webapp to see if the same instance is printed.

The servlet class:
package test;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

@javax.servlet.annotation.WebServlet(urlPatterns = "/*")
public class TestServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("singleton: " + MySingleton1.getInstance());
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
The content of test.war:
WEB-INF/classes/test/MySingleton1$1.class
WEB-INF/classes/test/MySingleton1.class
WEB-INF/classes/test/TestServlet.class
To deploy the webapp to GlassFish:
$ asadmin deploy test.war
$ asadmin deploy --name test2 test.war
$ asadmin list-applications
To run the 2 webapp and notice 2 different instances of MySingleton class are instantiated within the same JVM, one for each webapp:
$ curl http://localhost:8080/test/
singleton: test.MySingleton1@114536a5

$ curl http://localhost:8080/test/
singleton: test.MySingleton1@114536a5

$ curl http://localhost:8080/test2/
singleton: test.MySingleton1@3997ebf6

$ curl http://localhost:8080/test2/
singleton: test.MySingleton1@3997ebf6

Followers

Pageviews Last 7 Days