设计模式_单例模式
类的单例设计模式:采取一定的方法保证在整个系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态)。
单例模式的特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点;
单例模式的结构
单例模式的主要角色如下。
- 单例类:包含一个实例且能自行创建这个实例的类。
- 访问类:使用单例的类。
单例模式的实现
饿汉式
静态常量
class Singleton {
private Singleton() {// 构造器私有化,防止外部new
}
// 本类内部创建新实例
private final static Singleton instance = new Singleton();
public static Singleton getInstance() {// 提供一个共有的静态方法,返回实例对象
return instance;
}
}
优点:写法简单,在类加载的时候即完成实例化,避免线程同步问题。
缺点:在类加载的时候完成实例化,没有达到Lazy loading的效果。如果该类的实例从未使用,可能会造成内存浪费。
这种基于classloader机制避免了多线程的同步问题。Instance在类加载的时候就实例化,在单例模式中大多都是调用getInstance方法,但是导致类加载的原因有很多,因此在其他原因导致类加载的时候,初始化instance就没有达到lazy loading的效果。
静态代码块
class Singleton {
private Singleton() {// 构造器私有化,防止外部new
}
// 本类内部创建新实例
private static Singleton instance;
static {// 静态代码块中创建单例对象
instance = new Singleton();
}
public static Singleton getInstance() {// 提供一个共有的静态方法,返回实例对象
return instance;
}
}
和静态常量的方式一样,同样是指类加载的时候创建实例,也可能造成内存浪费。
懒汉式
线程不安全
class Singleton {
// 本类内部创建新实例
private static Singleton instance;
private Singleton() {// 构造器私有化,防止外部new
}
public static Singleton getInstance() {// 提供一个共有的静态方法,当使用到该方法到时候,采取新建实例对象
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
可以达到Lazy loading的效果,但是只能在单线程下使用。
多线程环境下,如果一个线程进入了
if(instance == null)
判断,还没有继续向下执行,另一个线程也完成了这个判定,这时就产生了多个实例。所有在多线程环境下不可使用。由于存在创建多个实例对象的可能,破坏了单例模式,开发中不可使用。
线程安全(同步方法)
class Singleton {
// 本类内部创建新实例
private static Singleton instance;
private Singleton() {// 构造器私有化,防止外部new
}
public static synchronized Singleton getInstance() {// 提供一个共有的静态方法,加入同步处理,解决线程安全问题
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
虽然通过同步方法的方式解决了线程安全问题,但是每一次getInstance()都需要进行同步,效率很低。
双重检查
class Singleton {
// 本类内部创建新实例
private static volatile Singleton instance;
private Singleton() {// 构造器私有化,防止外部new
}
public static Singleton getInstance() {// 提供一个共有的静态方法,加入双重检测,同时解决线程安全、Lazy loading和效率问题。
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查进行了两次if (instance == null)
的检测,保证了线程安全。实例化代码只执行一次,后续访问,if (instance == null)
判断之后会直接返回实例化对象,避免反复进行方法同步。推荐使用该方式。
变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
静态内部类
class Singleton {
// 本类内部创建新实例
private static volatile Singleton instance;
private Singleton() {// 构造器私有化,防止外部new
}
private static class SingletonInstance {//静态内部类,该类中有一个静态属性Singleton
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {// 提供一个共有的静态方法,直接返回SingletonInstance.instance。
return SingletonInstance.instance;
}
}
由于Jvm加载类的过程是线程安全的(整个过程都是同步的),而且在加载Singleton类的时候并不会同时加载其内部类SingletonInstance,只有在调用getInstance()方法的时候才会加载SingletonInstance类并切完成实例化。
这样利用Jvm保证了线程安全,同时达到了Lazy loading的效果。推荐使用该方式。
枚举
enum Singleton {
INSTANCE;//属性
public void saySomething() {
System.out.println("hello singleton.");
}
}
避免了多线程的同步问题,而且还能防止反序列化重新创建信的对象。
单例模式的应用场景
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
- 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。