设计模式之单例模式

  一、设计模式

  今天开始学习总结设计模式,首先GOF23种设计模式是国外“F4”总结的“经验之谈”,它其实是一种思想,用以在实际开发中对项目构建的一种参考,在一定程度上使用设计模式能够对程序有很大的提升,提升程序的性能、扩展性、可维护行等等,但是也要避免陷入“过度设计”的局面,这样就适得其反了,所以掌握好真正的精髓和思想就尤为重要了。

  二、单例模式

  都说单例模式是设计模式中最简单的几种设计模式之一,但是感觉要理解每一步的细节也并没有那么简单。

  • 目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。减少系统不断创建、销毁该对象的性能开销。
  • 做法:私有化构造方法,让该类自己提供实例,外部访问全局访问点,有实例则返回,无则创建。同时需要确保线程安全。

  单例模式的实现有多种方式:懒汉式、饿汉式、双检锁式、静态内部类式(登记式)、枚举式,下面来一一说明这些实现方式的优缺点及实现的代码:

  1. 懒汉式
    public class Singleton1 {
    
        private static Singleton1 instance;
        
        //私有化构造函数
        private Singleton1() {}
        
        public static synchronized Singleton1 getIntance() {
            
            if(instance==null) {
                instance = new Singleton1();
            }
            
            return instance;
        }
    }

    所谓懒汉式,主要特点就是对对象进行懒加载,也叫延迟加载,即在调用该类的getInstance方法时再进行创建对象。优点是在需要该实例的时候再进行创建,减少内存开销,因为作为单例对象,在创建的过程中往往需要消耗比其他对象更多的系统资源。缺点是调用的效率会低,因为创建对象需要一定的时间,并且为了保证线程安全加了synchronized关键字进行加锁,也降低了时间效率。

  2. 饿汉式
    /**
     * 单例模式之饿汉式 :线程安全(jvm保证在类加载过程只产生一个对象),
     * 调用效率高,但不是延迟加载
     * @author Administrator
     *
     */
    public class Singleton2 {
    
        private static Singleton2 instance = new Singleton2();
        
        //私有化构造函数
        private Singleton2() {
    
        }
        public static Singleton2 getInstance() {
        
            return instance;
        }
    }

    饿汉式在类声明对象属性的过程中就进行了创建对象,根据classLoader机制,保证了线程安全只会创建一个实例。可以看到饿汉式的实现过程相对简单且线程安全,但是并没有延迟加载。

  3. 双检锁(双重检验锁)
    /**
     * 单例模式之双检锁:线程安全、延迟加载,但调用效率低
     * @author Administrator
     *
     */
    public class Singleton3 {
    
        private static volatile Singleton3 instance;
        
        private Singleton3() {}
        
        public static Singleton3 getInstance() {
            
            if(instance==null) {//可能同时有多个线程进入到该语句,在此处等待进入
                synchronized(Singleton3.class) {
                    if(instance==null) {//二次校验,可能上一个获取锁的线程已经创建好了实例,故不需要再次创建
                        instance = new Singleton3();
                    }
                }
            }
            return instance;
        }
    }

    首先我们可以看到,双检锁是属于懒汉式的一种升级版,因为将synchronized关键字的范围缩小了,不必每次调用getInstance方法都需要同步,提高了调用时的效率。实现细节上可看代码注释。

  4. 静态内部类式
    /**
     * 单例模式之静态内部类实现:线程安全,延迟加载,调用效率高
     * @author Administrator
     *
     */
    public class Singleton4 {
    
        private static class SingletonHolder{
            private static final Singleton4 instance = new Singleton4();
        }
        
        private Singleton4() {}
        
        public static final Singleton4 getInstance() {
            
            return SingletonHolder.instance;
        }
    }

    静态内部类应该是前面几种方式中的最佳实践,它既通过classLoader机制保证了实例化对象时的线程安全,跟饿汉式相比,它还具有延迟加载的功能,我们知道饿汉式在类被装载时就会就会初始化静态的变量和方法,而静态内部类的方式只有显示的调用getInstance方法后才会实例化单例对象,所以做到了懒加载。我们需要注意的是,往往实例化我们要的单例对象是很消耗系统资源的,所以我们想在我们想要实例化的时候在进行实例化,而不是类被装载、类调用其他静态方法时而实例化单例对象,所以使用静态内部类的方式更加合理。

  5. 枚举式
    public enum Singleton5 {
    
        //该实例代表单例对象
        INSTANCE;
        
        //单例对象可以有自己的操作
        public void SingletonOperation() {
            //自己的操作
        }
        
    }

    这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
    这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
    不能通过 reflection attack 来调用私有构造方法。

    从上面的这段话中我们知道,单例模式的常用实现方式存在着安全漏洞,即通过反射机制和反序列化可以实例化多个不同的对象。那我们该如何去防止和处理这两个漏洞呢?我们的做法是首先实现序列化接口,然后再通过readResolve方法返回我们的实例,这样可以解决反序列化问题。通过再私有的构造方法中判断对象是否存在,已存在则抛异常的方式解决反射的问题:以下是静态内部类实现的方法解决相应问题

public class Singleton4 implements Serializable{

    private static class SingletonHolder{
        private static final Singleton4 instance = new Singleton4();
    }
    
    private Singleton4() {
        if(SingletonHolder.instance!=null) {
            throw new RuntimeException("can only create one object");
        }
    }
    
    public static final Singleton4 getInstance() {
        
        return SingletonHolder.instance;
    }
    
    public Object readResolve() throws Exception{
        return SingletonHolder.instance;
    }
}

具体的细节可以参考该博客:https://blog.csdn.net/hardwin/article/details/51477359

  以上就是我总结的设计模式之单例模式,如果有写的不对的地方,欢迎评论指出。

原文地址:https://www.cnblogs.com/ring2/p/11125741.html