单例模式(Singleton Pattern)
单例模式(Singleton Pattern)
确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。有时候创建对象时耗时过多或者耗资源过多,但是我们经常用这个对象。我们就可以使用单例模式,并且如果一个工具类存有状态,我们也需要单例,因为如果是多个实例,可能存在数据读取错误的问题,例如配置类,我们就需要单例它,大家去一个地方取就可以了,不需要多个创建。常见的写法
- 饿汉式单例
- 懒汉式单例
- 注册式单例
- Threadlocal单例
To explain some terms
High execution efficiency:
- classes is created ,When classes are been loading, if we want to get it just getting it and without to create it again
Thread safety:
- caused by the calsses that you want are already created,if you do not create it again, it is thread safety,isn't it!
- there are no situation that two threads get an one method that you use to creat classes.
Waste of memory:
- who can sure that the calsses are uesd certainly?
- if you don't use these classes, which means these are occupying memory.
Eager Singleton
public class HungrySingleton { private static final HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton() { } private HungrySingleton getInstance() { return hungrySingleton; } }
advantages:1.High execution efficiency 2.Thread safety
disadvantages:1.Waste of memory
To solve the problem of Waste of memory. . . .
Lazy Singleton
public class LazySimpleSingleton { private static LazySimpleSingleton lazySimpleSingleton; private LazySimpleSingleton() { } public static LazySimpleSingleton getInstance() { if (lazySimpleSingleton != null) { lazySimpleSingleton = new LazySimpleSingleton(); } return lazySimpleSingleton; } }
To test
public class ExecuteThread implements Runnable { public void run() { LazySimpleSingleton lazySimpleSingleton=LazySimpleSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + ":" + lazySimpleSingleton); } } public class LazySimpleSingletonTest { public static void main(String[] args) { Thread thread=new Thread(new ExecuteThread()); Thread thread2=new Thread(new ExecuteThread()); thread.start(); thread2.start(); } }
result:
Actually,Sometimes it produces the same results, sometimes it doesn't,which shows There are problem of security of Thread!
same results:
- normal execution sequence
The latter covers the former(2 Threads execute the same code,the former assign the classes but did not return,at this time,the latter began to execute,so latter covers the former.in fact, there are two classes were created )
different results:
- 2 Threads execute the same code,the former have returned,the the latter returned
tips: the red one is what i said the same code executed
if (lazySimpleSingleton != null) { lazySimpleSingleton = new LazySimpleSingleton(); }
advantages: Save memory
disadvantages: Thread unsafe
To solve the problem of Thread of unsafe. . . . (easily,To put a key word of synchronized in front of "static LazySimpleSingleton" like ->(
public synchronized static LazySimpleSingleton getInstance())
),but cause a problem that all of the others threads are waiting, the picture below shows that if one thread get a source,which means others can not get the same source,all others will be blocked,in this way,all of others will be waiting and one by one to execute
To solve the problem of performance--------------------------->
public class LazyDoubleCheckSingleton { private static LazyDoubleCheckSingleton lazySimpleSingleton; private LazyDoubleCheckSingleton() { } public static LazyDoubleCheckSingleton getInstance() { // To check whether we should to create or not,if already the class is create, the left of others do not need to execute code below(solve problem of performance) if (lazySimpleSingleton == null) { synchronized (LazyDoubleCheckSingleton.class) { // if two threads enter into, what should we do,so Let's make a judgment here if (lazySimpleSingleton == null) { lazySimpleSingleton = new LazyDoubleCheckSingleton(); } } } return lazySimpleSingleton; } }
To explain:
first of all,all threads can be allowed to enter into the method, and if lazySimpleSingleton == null the threads can enter into(solve the problem of performance->do not need to wait), then to use 'synchronized ' to limit problem of Thread unsafe,but if more than one thread enter into and one of these has executed into ending of block of 'if', definitely, the other still will created,so we make a judgement again(resolve the problem of thread unsafe )
but !!! the method will not be elegant enough.. it is hard to read,and produce problem 'Tag and Seek',so we add 'volatile' in front of 'static LazyDoubleCheckSingleton' like
private volatile static LazyDoubleCheckSingleton lazySimpleSingleton;
To solve the problem of not enough elegant--------------------------->
public class LazyStaticInnerClassSingleton { public LazyStaticInnerClassSingleton() { // what it is essential to creating the class is to use Constructor to create // so if someone wants to use Constructor to create,we just throw Exception to avoid illegal create throw new RuntimeException("forbid to create...."); } public LazyStaticInnerClassSingleton getInstance() { return LazyHolder.lazyStaticInnerClassSingleton; } private static class LazyHolder { private static final LazyStaticInnerClassSingleton lazyStaticInnerClassSingleton = new LazyStaticInnerClassSingleton(); } }
To explain:
apparently!we use mechanism of class loading,it is well known that during the classess the inner classess are not loaded,when we get the inner class that we want,than the inner class that we want will be load! but all of methods above will be destory by reflect!! what the essence that to use the reflect to create the classess is to use Constructor!so we just need to throw new RuntimeException in Constructor to avoid illegal create , but what i do still not enough decent,who konw the reason why we thrown an exception at the Constructor?
To solve the problem of not enough decent--------------------------->
Register Singleton(container Singleton)
public enum EnumSingleton { INSTANCE; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public Object data; public static EnumSingleton getInstance(){ return INSTANCE; } } public class User { private String userName; }
To Test
public class EnumSingletonTest { public static void main(String[] args) { EnumSingleton instance = EnumSingleton.getInstance(); instance.setData(new User()); Class<EnumSingleton> enumSingletonClass = EnumSingleton.class; try { Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class,int.class); constructor.setAccessible(true); EnumSingleton enumSingleton = constructor.newInstance(); System.out.println(enumSingleton); } catch (Exception e) { e.printStackTrace(); } } }
To explain:
the image illustrate that we are not able to create a class with reflection
we find the class of Constructor has wrote ' if the modifier is emum than the class can not be create'
but, why we say that the method must be Thread safety,we still can skim the souce code->(Enum#valueOf),as soon as we create the Enum the 'INSTANCE ' will be put in the container,so Thread safety
so, in some extent , it will produce a new problem of Waste of memory,(put these INSTANCE into container in advance)
To solve the problem of not Waste of memory---------------------------> (we can simluate do it as what IOC does)
public class ContainerSingleton { ContainerSingleton() { } private static Map<String, Object> iocMock = new ConcurrentHashMap<String, Object>(); public static Object getInstance(String className) { Object instance = null; if (!iocMock.containsKey(className)) { try { instance = Class.forName(className).newInstance(); iocMock.put(className, instance); } catch (Exception e) { e.printStackTrace(); } return instance; } return iocMock.get(className); } } public class Pojo { String mock; }
To test
public class ContainerSingletonTest { public static void main(String[] args) { Object pojo = ContainerSingleton.getInstance("Pojo"); Object pojo1 = ContainerSingleton.getInstance("Pojo"); System.out.println(pojo==pojo1); } }
actually, serialization can destory singleton!!!!!
To solve the problem of destruction the singleton with serialization --------------------------->
public class SerializableSingleton implements Serializable { final static SerializableSingleton INSTANCE = new SerializableSingleton(); private SerializableSingleton() { } public static SerializableSingleton getInstance() { return INSTANCE; } private Object readResolve() { return INSTANCE; } }
To test
public class SerializableSingletonTest { public static void main(String[] args) { SerializableSingleton s1 = null; SerializableSingleton s2 = SerializableSingleton.getInstance(); FileOutputStream fos = null; try { fos = new FileOutputStream("D:\\growingup\\growup\\target\\SerializableSingleton.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); // output the file into disk oos.writeObject(s2); oos.flush(); oos.close(); //input the file in disk into memory FileInputStream fis = new FileInputStream("D:\\growingup\\growup\\target\\SerializableSingleton.obj"); ObjectInputStream ois = new ObjectInputStream(fis); s1 = (SerializableSingleton)ois.readObject(); ois.close(); System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); } catch (Exception e) { e.printStackTrace(); } } }
To explain:
first we output the file into disk and input the file in disk into memory again,the result is true(still is a same class)the key is #readResolve,we still skim the souce of code(code java.io.ObjectInputStream#readObject0
after we have read can get that information of there is a judgment if you have a method named readResolve then to get with reflection, otherwise,to create a new instance.so we must be set a method called readResolve,which can help us to resolve problem of serialization with souce of code of java.lang.ClassNotFoundException
The last pattern of singleton i give
Threadlocal pattern of singleton(be ensure globally unique in the same Thread )
public class ThreadLocalSingleton { static final ThreadLocal<ThreadLocalSingleton> threadLocalSingletonThreadLocal = new ThreadLocal<ThreadLocalSingleton>() { @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton() { } public static ThreadLocalSingleton getInstance() { return threadLocalSingletonThreadLocal.get(); } } public class ExecuteThreadLocal implements Runnable { public void run() { ThreadLocalSingleton threadLocalSingleton=ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName() + ":" + threadLocalSingleton); } }
To test
public class ThreadLocalSingletonTest { public static void main(String[] args) { System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); Thread thread=new Thread(new ExecuteThreadLocal()); Thread thread2=new Thread(new ExecuteThreadLocal()); thread.start(); thread2.start(); } }
To explain
the image above shows there are different threads get different results,but we can get a same instance in same thread,example is main thread(we get the same result in 3 times),we can find reason with resource of code of java.lang.ThreadLocal#get.
Store on a per-thread basis
practical application in souce of code
- org.springframework.beans.factory.config.AbstractFactoryBean#getObject
- org.apache.ibatis.executor.ErrorContext#instance
Sum up
advantages:
- ther is just one instance in memory,which is efficient to diminish Memory overhead
- Access is effectively controlled
disasvantages:
there is no interface,so that it is difficult to expand(if we want to extend function,we have to modify original code)