Java设计模式之单例模式
单例模式,顾名思义就是从始至终只创建一个对象,下面介绍下单例模式创建的注意事项和常见单例模式的写法
一:单例模式创建时的注意事项
1.构造函数私有(由private修饰)
2.多线程情况下的线程安全问题
3.延时加载的问题
4.序列化和反序列化时如何保证单例
5.预防反射攻击
二:单例模式的创建方式
1.饿汉式,类加载时对象被创建,特点是:无需考虑线程安全问题,访问效率高,无法延时加载。
public class HungrySingleton { private static final HungrySingleton hungry = new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungry; } }
2.懒汉式,对象被访问时创建,特点是:需考虑线程安全问题,访问效率低,可延时加载。
public class LazySingleton { private static LazySingleton singleton; private LazySingleton(){ } private static synchronized LazySingleton getInstance(){ if(singleton == null){ singleton = new LazySingleton(); } return singleton; } }
3.懒汉式-双锁检测,懒汉式的改进版,加锁的操作放到了获取实例方法的内部,特点是:需考虑线程安全,访问效率低,可延时加载。
public class LazyDoubleCheckSingleton { private static volatile LazyDoubleCheckSingleton singleton; private LazyDoubleCheckSingleton(){} private static LazyDoubleCheckSingleton getInstance(){ if (singleton == null) { synchronized (LazyDoubleCheckSingleton.class){ if (singleton == null) { singleton = new LazyDoubleCheckSingleton(); } } } return singleton; } }
4.静态内部类,采用静态内部类的方式创建,特点是:无需考虑线程安全,访问效率高,可延时加载。
public class InnerClassSingleton { private InnerClassSingleton(){} public static InnerClassSingleton getInstance(){ return InnerClass.INSTANCE; } private static class InnerClass{ private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } }
5.枚举式单例,采用定义枚举类的方式来实现单例。
public enum EnumSingleton { INSTANCE; // 该属性为扩展点 private Object data; public static EnumSingleton getInstance(){ return INSTANCE; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
6.注册式单例,spring中最常用的使用方式。
public class BeanFactorySingleton { private BeanFactorySingleton(){} private static Map<String, Object> ioc = new ConcurrentHashMap<>(); public static Object getInstance(String className) { synchronized (ioc){ if (!ioc.containsKey(className)) { try{ ioc.put(className, Class.forName(className).newInstance()); }catch (Exception e){ e.printStackTrace(); } } return ioc.get(className); } } }
7.ThreadLocal实现,该种方式创建的只能保证一个线程内使用的是同一个对象。
public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> instance = new ThreadLocal<ThreadLocalSingleton>(){ @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton(){} public static ThreadLocalSingleton getInstance(){ return instance.get(); } }
三:上面列出了7种创建单例模式的方法,但是存在无法保证序列化和反序列化带来的对象不一致以及反射攻击的问题,下面给出这两种的问题的一个解决方案(以懒汉式为例)。
public class LazySingleton { private static LazySingleton singleton; private LazySingleton(){ // 防止反射攻击 if (singleton != null) { throw new RuntimeException("instance has create"); } } private static synchronized LazySingleton getInstance(){ if(singleton == null){ singleton = new LazySingleton(); } return singleton; } // 解决序列化和反序列化造成的对象创建不一致 private Object readResolve(){ return getInstance(); } }
四:单例模式的优缺点。
优点:
1.内存中只存在一个实例,减少内存开销。
2.避免对资源的多重占用。
3.设置全局访问点,严格控制访问。
缺点:
1.无接口,扩展起来困难。修改对象时,只有修改代码,无其它途径。