public enum EnumSingleton implements Calc {A traditional, non-enum implementation of eager-initialization singleton is:
INSTANCE;
public int add(int a, int b) {
return a + b;
}
}
public class EagerSingleton {A lazy-initialization singleton implemented with double-checked locking (DCL), which works in Java 5 onward:
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() { return instance; }
}
package test.concurrent;A lazy-initialization singleton implemented with holder idoim (applicable to lazy static field):
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;
}
package test.concurrent;A main method that spawns multiple threads calling getInstance():
public final class MySingleton2 {
private MySingleton2() {}
private static class Holder {
private static final MySingleton2 instance = new MySingleton2();
}
public static MySingleton2 getInstance() {
return Holder.instance;
}
public static void main(String[] args) {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().
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();
}
}
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.classTo deploy the webapp to GlassFish:
WEB-INF/classes/test/MySingleton1.class
WEB-INF/classes/test/TestServlet.class
$ asadmin deploy test.warTo run the 2 webapp and notice 2 different instances of MySingleton class are instantiated within the same JVM, one for each webapp:
$ asadmin deploy --name test2 test.war
$ asadmin list-applications
$ 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