Singleton
Java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。
单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自己自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。
饿汉式单例类
class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton getInstance() { return instance; } }
懒汉式单例类
class LazySingleton { private static LazySingleton instance; private LazySingleton() { } public static synchronized LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
饿汉式在类被加载的时候会自己实例化,从资源利用效率来看,比懒汉式稍差了些。从速度和反应的时间来看,比懒汉式稍好些。
懒汉式必须处理在多个线程首次引用此类的访问限制,也必然涉及资源初始化。消耗相当大的时间。
因为饿汉式在不易在 C++ 实现,因为 C++ 静态初始化没有固定的顺序,可能会出现问题。所以 Gof 提出单例模式概念时,举例都是用懒汉式。
登记式单例类
class RegSingleton { private static Map<String, RegSingleton> registy = new HashMap<String, RegSingleton>(); static { RegSingleton x = new RegSingleton(); registy.put(x.getClass().getName(), x); } protected RegSingleton(){ } public static RegSingleton getInstance(String id){ if(StringUtils.isEmpty(id)){ id = "org.xl.singleton.RegSingleton"; } if(!registy.containsKey(id)){ try { registy.put(id, (RegSingleton)Class.forName(id).newInstance()); } catch (Exception e) { e.printStackTrace(); } } return registy.get(id); } }
class RegSingletonChild extends RegSingleton{ public static RegSingletonChild getInstance(){ return (RegSingletonChild)RegSingleton.getInstance("org.xl.singleton.RegSingletonChild"); } public void about(){ System.out.println("hello, i am RegSingletonChild !"); } }
登记式单例 由于子类必须允许父类以构造方法产生实例,因为它的构造方法是公开的。这样一来等于允许以这样的方式产生实例而不在父类中登记。这是登记式的缺点。
父类的实例必须存在才可能有子类的实例,也是一种浪费,也是另一个缺点。