五、单例模式(Singleton Pattern)《HeadFirst设计模式》读书笔记

  1.单例模式的定义:确保一个类只有一个实例,并提供一个全局访问点。

    编写单例实例的要点是:

    1)将构造方法私有化,确保别的类不能通过构造方法创建对象;

    2)在本类中通过私有化的构造方法创建对象;

    3)提供一个静态方法让其它类可以获取这个对象。

    使用静态方法是因为普通方法要通过对象来调用,而其它类不能获取到对象,所以只能通过静态方法调用。

  2.单例模式可以分为两种类型:饿汉式和懒汉式。

    1)饿汉式

      饿汉式非常饥渴,在类初始化时就已经将对象创建好了。

public class HungerSingleton {
    private static HungerSingleton singleton = new HungerSingleton();
    private HungerSingleton(){
    }
    public static HungerSingleton getInstance() {
        return singleton;
    }
}

      饿汉式同样可以用枚举来简洁的实现:

public enum SingletonEnum {
    INSTANCE
}

      在该枚举中只声明一个INSTANCE变量,它就是单例的,使用时可以通过枚举类名.变量名来调用。

    2)懒汉式

      懒汉式非常懒,只有在其它类调用了获取对象的方法时才创建对象,其实就是懒加载。

public class LazySingleton {
    private static LazySingleton singleton;
    private LazySingleton(){
    }
    public static LazySingleton getInstance() {
        //判断Singleton对象是否已经创建,如果为null就先创建,不为null就直接返回
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

  3.懒汉式单例模式的问题和改进

    在多线程的环境下,如果多个线程想要获取对象实例,可能会产生线程安全问题:如果线程A判断singleton==null后释放了CPU的使用权,由线程B再执行全部过程,则线程B创建好对象之后,线程A又会创建一个新的对象,这样就不是单例了。下面提供了几种解决方法:

    1)对getInstance()方法加上synchronized关键字,这样虽然保证了线程安全,但是效率却大大降低,因为实际上只有第一次Singleton对象为null时才会产生线程安全问题(因为不为null时就直接返回了,不会执行if里面的创建对象代码);

    2)双重检测加锁。在1)的基础上,在synchronized的外层再加一重Singleton对象是否为null的判断,用于第一次为null时对象的创建,同时将类变量声明成volatile,防止指令重排产生的问题。这样只有第一次为null的时候会执行synchronized代码块,后面单例对象已经创建好了会执行外层if里的代码而直接返回了。

public class DoubleCheckLazySingleton {
    private static volatile DoubleCheckLazySingleton singleton;
    private DoubleCheckLazySingleton(){
    }
    public static DoubleCheckLazySingleton getInstance() {
        if (singleton == null) {
            synchronized(DoubleCheckLazySingleton.class){
                if (singleton == null) {
                    singleton = new DoubleCheckLazySingleton();
                }
            }
        }                                 
        return singleton;
    }
}

    3)静态内部类

      在静态内部类中声明静态变量并创建对象,并在单例类中提供获取实例的方法,通过类名.变量名的方式获取到这个唯一的实例,因为只有在该方法被调用时静态内部类才会被加载并创建实例,因此这种方式是一种线程安全的懒汉式单例模式。

public class InnerStaticClassSingleton {
    private InnerStaticClassSingleton() {
    }
    //静态内部类
    private static class InnerClass{
        private static InnerStaticClassSingleton singleton = new InnerStaticClassSingleton();
    }
    public static InnerStaticClassSingleton getInstance(){
        return InnerClass.singleton;
    }
}    

  4.总结

    1)单例模式就是将构造方法私有化,再在类内部创建对象并提供获取该对象的静态方法;

    2)单例模式分为懒汉式和饿汉式,懒汉式可能会产生线程安全问题,可以通过加锁或者双重检测或静态内部类来解决。如果并不需要频繁的获取对象,只需要简单的加锁就可以了,效率也不会低太多;

    3)单例的类因为构造方法都是私有化的,因此不能被继承,如果需要继承只能通过修改构造方法的权限修饰符,这就不是严格的单例了;此外单例模式是利用静态变量实现的,继承就意味着所有子类都共享这一变量,因此涉及单例的继承时要谨慎考虑。

posted @ 2020-07-04 10:06  ADvancedCZ  阅读(120)  评论(0编辑  收藏  举报