Java设计模式之创建型模式-单例模式(Singleton)

单例模式(Singleton): 保证一个类仅有一个实例,并且提供一个访问它的全局访问点。


单例模式实现的三个基本步骤:

  1. 私有构造方法来限制外部类对其直接实例化
  2. 提供一个私有静态当前类的对象作为变量
  3. 提供一个公有静态方法返回类的实例

单例模式实现的几种方法:

懒汉式

  懒汉式单例模式在类创建的时候不创建实例,因此类加载的速度比较快。但是在运行时获取对象需要创建实例,此时速度相对较慢。换句话说,懒汉式单例模式在第一次调用的时候才初始化,这样做避免了内存的浪费。
  以下是使用此方法创建的LazySingleton类实例代码。

public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {

    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

  这种方式在单线程的环境下可以很好的处理,但是在多线程的环境下,如果多个线程同时访问LazySingleton类,调用getInstance()方法时,多个线程会有可能获得单例类的不同实例从而破坏了单例模式。
  为了使上述方法变得线程安全,在多线程的环境下也满足我们的要求,现对类的全局方法进行同步处理,更改后的代码如下。

public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {

    }

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

  上述两段代码的差异仅在于getInstance()方法是否有关键字synchronized修饰做同步处理来保证线程安全性。虽然说同步方法保证了线程安全性,但是你会发现我们每次调用一次getInstance()方法时都需要加锁一次,影响效率。
  为了避免每次的额外的开销,使用双重锁定,先看下代码的实现。

public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {

    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

  这里做了两次instance == null的判断,有什么目的?
  第一次instance == null的判断可以避免线程每次都进行一次加锁,只是在实例未被创建的时候去进行加锁处理,减少开销。
  第二次instance == null的判断是为了防止多个线程同时调用getInstance()方法且都满足第一次instance == null的判断的这种情况,此时若没有第二次instance == null判断,所有线程排队进入锁区执行new LazySingleton()创建新的实例,达不到单例的要求。

饿汉式

  饿汉式单例模式正好与懒汉式单例模式相反,在类加载的时候就创建了实例,完成了初始化,所以类加载比较缓慢,而在运行时获取对象的速度相对较快。
  以下是使用此方法创建的LazySingleton类实例代码。

public class HungrySingleton {

    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {

    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

  饿汉式单例模式相对比较常用,因为它不需要同步,执行的效率也相对较高。但是问题就是在类加载的时候就进行初始化,没有达到懒加载的效果,浪费内存。
  现在对如上代码做相应的调整。

public class HungrySingleton {

    private static class HungrySingletonHolder {
        private static final HungrySingleton INSTANCE = new HungrySingleton();
    }

    private HungrySingleton() {

    }

    public static final HungrySingleton getInstance() {
        return HungrySingletonHolder.INSTANCE;
    }
}

  在这个方法中,用一个私有内部的静态类来包含我们的单例类实例。当单例类加载的时候,HungrySingletonHolder类不会加载到内存当中,只有当我们调用getInstance()方法的时候,这个静态内部类才会被加载并且创建HungrySingleton类的实例。如果实例化很消耗资源,想让它延迟加载但又不希望在类加载的时候就实例化,那么这种方式就很合适。

你可能感兴趣的:(设计模式)