The Singleton Pattern ensures a class has only one instance, and provides a global point of access to it.
I will demonstrate two ways of creating singleton classes in java in the first approach we are going to use the traditional getInstance which is a public static factory method.All calls to ThreadPool.getInstance return the same object reference, and no other ThreadPool instance will ever be created.One advantage of the factory-method approach is that it gives you the flexibility to change your mind about whether the class should be a singleton without changing its API.
To make a singleton class serializable it is not sufficient merely to add implements Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method. Otherwise, each time a serialized instance is deserialized, a new instance will be created.
The second approach we will be sing enums to create singleton classes. This approach is more concise, provides the serialization machinery for free, and provides a guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks.
Note : a privileged client can invoke the private constructor reflectively with the aid of the AccessibleObject.setAccessible method. If you need to defend against this attack, modify the constructor to make it throw an exception if its asked to create a second instance.
Lets say we need a singleton class which will return the same thread pool and we need to make sure we can create only one instance of ThreadPool class.
First Approach
Using a public static factory method we have made the class implement serializable to demonstrate the usage of the same though it doesn’t make sense here.
import java.io.Serializable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPool implements Serializable { private static final long serialVersionUID = 232323; private static transient final ExecutorService executorService = Executors .newFixedThreadPool(ThreadPoolCalculator.getOptimalThreadCount()); private ThreadPool() { if (executorService != null) { throw new AssertionError(); } } public static ExecutorService getInstance() { return executorService; } private Object readResolve() { return executorService; } }
public class ThreadPoolCalculator { private ThreadPoolCalculator() { super(); } public static int getOptimalThreadCount() { int numberOFThreads = 50; int numberOFCPU = Runtime.getRuntime().availableProcessors(); int targetCPUUtilization = 1; int ratioWC = 50; numberOFThreads = numberOFCPU * targetCPUUtilization * (1 + ratioWC); return numberOFThreads; } }
Second Approach using enums
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public enum ThreadPoolEnum { INSTANCE(Executors.newFixedThreadPool(getOptimalThreadCount())); ThreadPoolEnum(ExecutorService executorService) { this.executorService = executorService; } private ExecutorService executorService; public ExecutorService getExecutorService() { return executorService; } public static int getOptimalThreadCount() { int numberOFThreads = 50; int numberOFCPU = Runtime.getRuntime().availableProcessors(); int targetCPUUtilization = 1; int ratioWC = 50; numberOFThreads = numberOFCPU * targetCPUUtilization * (1 + ratioWC); return numberOFThreads; } }
Driver class
public class Driver { public static void main(String[] args) { ThreadPoolEnum.INSTANCE.getExecutorService().execute(new Test()); ThreadPool.getInstance().execute(new Test()); } }
I will add One more example which i came across in my project . We had a use case where we had to fetch MetricMetaData from one of table and as this data was required across application we didn’t wanted to have more than one instance of this at any point considering the efficiency and memory constraints.I am not adding the dependent code as the focus is to show the singleton pattern usage.
public class MetricMetaTableCommand extends QueryCommand { private static final List<MetricMetadata> list = execute(new Object[] { "parameter1", "parameter2" }); private MetricMetaTableCommand(Database dbtype) { if (list.size() > 0) { throw new AssertionError(); } } public static List<MetricMetadata> getMetricData() { return list; } @SuppressWarnings("unchecked") @Override private final static List<MetricMetadata> execute(Object[] parameters) { ConfigFile scriptFile = new ConfigFile(Constants.QUERY_METRICMETADTA, FileType.script); String script = scriptFile.getFileContent(); Database dbtype = new Database("oracle"); return dbtype.querySchema(script, parameters, new MetricMetadataMapper()); } }