应用场景:
如果创建某个对象将会耗费过多的资源,或者需要某个对象作为某种设备或者其他对象的管理者的时候,比如对项目配置文件或者数据库进行读写的情况。
单例模式的定义:
确保某个类只有一个实例,并且自行实例化后向系统提供这个实例,单例的主要思想是通过将构造方法私有化从而将创建对象的权利回收,不让外部其他对象显式调用构造方法创建对象。
单例写法:
单例有五写法:
分别是饿汉式,懒汉式,双重判空检查法,内部类,枚举法:
- 饿汉式: 在有多个虚拟机的情况下有可能会创建多个实例
public class SingletonHungry { private SingletonHungry(){} private static final SingletonHungry mInstance = new SingletonHungry(); public static final SingletonHungry getInstance() { return mInstance; } public void sayHello() { System.out.print("Hello I am "+ SingletonHungry.class.getSimpleName()); } }
|
懒汉式:在多线程的情况有可能会创建多个实例,解决方法:加synchronized
public class SingletonLayzy {
private static SingletonLayzy mInstance = null; private SingletonLayzy() {} public static synchronized SingletonLayzy getInstance() { if( mInstance == null ) { mInstance = new SingletonLayzy(); } return mInstance; } public void sayHello() { System.out.println("Hello I am " + SingletonLayzy.class.getSimpleName()); } }
|
双重校验锁:
public class SingletonDoubleCheck {
private static volatile SingletonDoubleCheck mInstance = null; private void SingletonDoubleCheck() { } public static SingletonDoubleCheck getInstance() { if(mInstance == null) { synchronized(SingletonDoubleCheck.class) { if(mInstance == null) { mInstance = new SingletonDoubleCheck(); } } } return mInstance; } public void sayHello() { System.out.println("Hello I am "+ SingletonDoubleCheck.class.getSimpleName()); } }
|
类级内部类
public class SingletonHolder { private SingletonHolder() { } private static class InnerClass { private static SingletonHolder mInstance = new SingletonHolder(); } public static SingletonHolder getInstance() { return InnerClass.mInstance; } public void sayHello() { System.out.println("Hello I am "+ SingletonHolder.class.getSimpleName()); } }
|
枚举单例:
public enum SingletonEnum { INSTANCE; private SingletonEnum() {
} public String sayHello () { return "Hello I am " + SingletonEnum.class.getSimpleName(); } }
|
单例池:
public class SingletonPool {
public static final String SINGLETONE_KEY_PREFIX = "sigleton_prefix"; private static final int MAX_POOL_SIZE = 10; private static int mCurrentItemId = 0; private static Map<String, SingletonPool> mSingletonPools = new HashMap<>();
private SingletonPool() {
}
public static SingletonPool getInstance() { String key = SINGLETONE_KEY_PREFIX + mCurrentItemId; SingletonPool item = mSingletonPools.get(key); if ( item == null ) { item = new SingletonPool(); System.out.println("New Item"); mSingletonPools.put(key, item); } else { System.out.println("Get From Cache"); } mCurrentItemId = (mCurrentItemId + 1) % MAX_POOL_SIZE; return item; }
public String sayHello() { return "Hello I am " + this.toString(); } }
|
总结:单例共有五种写法分别是懒汉,饿汉,双重校验锁,类级内部类,枚举。
饿汉:因为加载类的时候就创建实例,所以线程安全。缺点是不能延时加载,同时多个ClassLoader存在时不能保持只有一个实例。
懒汉:需要加锁才能实现多线程同步,但是效率会降低。优点是延时加载。
双重校验锁:instance = new singletone()这种代码在不同编译器上的行为和实现方式不可预知,不推荐使用
类级内部类:延迟加载,减少内存开销。
枚举:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,但是失去了类的一些特性。