We will be able to create a copy of the Object by cloning it using the Object’s clone methodThe singleton pattern must be carefully constructed in multi-threaded applications. Double check locking is famous idiom and due to that there were java memory model changes in java 1.5 release. let's see how it happens before java 1.5 release even if you declare instance as volatile.
Double Check Lock
public static Singleton getInstance() {
if (instance == null){ // 0
synchronized(Singleton.class) { // 1
if (instance == null){ // 2
instance = new Singleton(); // 3
} } }
return instance; // 4
}
This alone is not enough. Here is famous article on this - DCL broken. Nice presentation before Java memory model changes in java 1.5
Singleton class with making instance as volatile
public class Singleton{
private static volatile instance; //volatile variable
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
Singleton class with Initialize on demand Holder Class Idiom
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() { }
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Singleton class with ENUM
public enum Singleton {
INSTANCE;
public void execute (String arg) {
//... perform operation here ...
}
}
If you are using multiple class loaders, this could defeat the Singleton implementation and result in multiple instances. If you want a true
Singleton
across class loaders, then you need a common parent to load the class in question, or you need to specify the class loader yourself. Multiple class loaders are commonly used in many situations—including servlet containers—you can wind up with multiple singleton instances no matter how carefully you've implemented your singleton classes. If you want to make sure the same class loader loads your singletons, you must specify the class loader yourself. private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
If SingletonClass implements the java.io.Serializable interface, the class's instances can be serialized and deserialized. However, if you serialize a singleton object and subsequently deserialize that object more than once, you will have multiple singleton instances
To avoid the above you need to implement readResolve() method.
private Object readResolve() {
return INSTANCE;
}
We will be able to create a copy of the Object by cloning it using the Object’s clone method SingletonDemo clonedObject = (SingletonDemo) obj.clone(); So to deal with this we need to override the Object’s clone method which throws a CloneNotSupportedException exception.
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
Breaking with Reflection
Singleton pattern can be broken using reflection, as shown below.
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Singleton s = Singleton.getInstance();
Class clazz = Singleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Singleton s2 = (Singleton) cons.newInstance();
}
}
Java’s own way of handling such mischievous behavior is to use a SecurityManager which restricts the ‘supressAccessChecks’permission of java.lang.reflect.ReflectPermission. The default security manager implementation does it. Following example shows this:
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
SecurityManager mgr = new SecurityManager();
System.setSecurityManager(mgr);
Singleton s = Singleton.getInstance();
Class clazz = Singleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Singleton s2 = (Singleton) cons.newInstance();
}
}
This would throw the exception while trying to create the second instance using Reflection.
Exception in thread "main" java.security.AccessControlException: access denied (java.lang.reflect.ReflectPermission suppressAccessChecks)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.reflect.AccessibleObject.setAccessible(AccessibleObject.java:107)
at com.test.singleton.securitymgr.Test.main(Test.java:17)
But the downside of this is that some of the libraries that we use, for example ‘Hibernate’ relies on reflective access to object properties. When we mark a field (instead of public getter / setter) with @Id or @Column annotation, Hibernate uses reflection to access the particular field to obtain the meta-data. So if we put a security manager which restricts reflective access, theoretically Hibernate should fail. One approach to solve this problem is to do the constructor checks to see if the instance variable is already set. If it is, then the constructor would throw an exception avoiding the instantiation.
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// Check if we already have an instance
if (INSTANCE != null) {
throw new IllegalStateException("Singleton" +
" instance already created.");
}
System.out.println("Singleton Constructor Running...");
}
public static final Singleton getInstance() {
return INSTANCE;
}
}
No comments:
Post a Comment