单例模式
概念
有时对于系统中某些类而言,不需要多个实体。一方面需要保证这个实体的“原子性”,另一方面也可以节省系统资源。比如对于游戏账号这个类来说,账号里某些信息是恒定不变的,用户上线下线登陆的还是那个账号,这时可以考虑单例模式。
- 定义: 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。 结构图如下:
实例
对于单例模式而言,要求我们在系统中只能获得一个实例。很容易想到以下写法:
首先将此类构造器设置为访问权限设置为private,禁止外部创建此对象。然后建立个私有静态成员变量instance存储实例,最后建立共公有静态成员方法,返回实例。
但是在高并发访问环境下,还是可能会出现多个实例的情况。原因是当第一次调用getInstance()时并触发instance=null时,程序执行instance = new SimpleSingleton(),如果SimpleSingleton对象初始化时间足够长,并且外部又出现第二次调用getInstance(),此时由于SimpleSingleton对象还在初始化中,还是会触发instance=null,进入循环体第二次执行instance = new SimpleSingleton()。
下面介绍三种单例模式都可以从各方面来解决这个问题。
- 恶汉式单例类1234567891011121314/*** 饿汉单例*/public class EagerSinleton {private final static EagerSinleton instance = new EagerSinleton();private EagerSinleton () {}public static EagerSinleton getInstance() {return instance;}}
恶汉单例模式主要是通过final关键字来实现的,当类EagerSinleton加载时静态变量instance执行初始化,final可以保证instance的指向不会改变。缺点是类加载时实例就会创建,可能会对系统资源开销造成影响。
- 懒汉式单例类
|
|
可以看到懒汉单例模式就是上述SimpleSingleton的线程安全版本。当需要此对象时对象才会被创建,也就是所谓的懒加载(LazyLoad)。
在getInstance方法中用synchronize关键字对LazySinleton.Class对象加锁,防止该对象同时被两个线程访问。instance采用volatile关键字修饰,保证此变量的对其他的线程可见性。多加了一层判断保证程序稳定性。缺点是必须处理线程锁,初次加载时可能消耗性能。
- Initialization Demand Holder (IoDH)12345678910111213141516/*** Initialization Demand Holder (IoDH)*/public class Singleton {private Singleton () {}private static class HolderClass {private final static Singleton instance = new Singleton();}public static Singleton getInstance () {return HolderClass.instance;}}
上述单例采用静态内部类的方式来实现,依赖于java语言特性,将恶汉,懒汉特性结合。
总结
缺点
- 职责过重,在一定程度上违背单一职责原则。
- 对于带GC回收机制的语言来说,长时间不用的对象有可能被回收,可能导致单例对象状态丢失。
适用场景
- 系统只需要一个实例