美文网首页
单例模式

单例模式

作者: Teemo_fca4 | 来源:发表于2020-05-07 21:07 被阅读0次

    单例设计模式:采取某种方法,对某个类在整个系统中,只有一个对象实例。比如Hibernate 的SessionFactory,一个数据源只需要一个SessionFactory就够了。
    适用于频繁创建和销毁的对象,工具类对象,频繁使用数据库或文件的对象(比如数据源,session工厂等)

    单例模式 可以细分为以下几种

    • 饿汉式(静态常量)
    • 饿汉式(静态代码块)
    • 懒汉式(线程不安全)
    • 懒汉式(同步方法,线程安全)
    • 懒汉式(同步代码块,线程不安全)
    • 双锁检验
    • 静态内部类
    • 枚举
    饿汉式(静态常量)
    public class SingletonTest01 {
        public static void main(String[] args) {
            //测试
            Singleton instance = Singleton.getInstance();
            Singleton instance2 = Singleton.getInstance();
            System.out.println(instance == instance2); // true
            System.out.println("instance.hashCode=" + instance.hashCode());
            System.out.println("instance2.hashCode=" + instance2.hashCode());
        }
    }
    
    //饿汉式(静态变量)
    class Singleton {
        //1. 构造器私有化, 防止外部new
        private Singleton() {}
        
        //2.本类内部创建静态对象实例
        private final static Singleton instance = new Singleton();
        
        //3. 提供一个公有的静态方法,返回实例对象
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    分析:
    这种写法非常简单,没有线程问题,利用类装载的时候创建对象,但是导致类装载的时机是很多的,因此资源浪费的问题。
    资源浪费不是特别严重的问题,因此总体是可以接受的,是可用的。

    饿汉式(静态代码块)
    class Singleton {
        private Singleton() {}//构造方法私有化,防止外部new
    
        //2.本类内部创建对象实例
        private  static Singleton instance;
        
        static { // 在静态代码块中,创建单例对象
            instance = new Singleton();
        }
        
        //3. 提供一个公有的静态方法,返回实例对象
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    这种方法和上面的几乎一样,只不过将对象实例化的过程放到了代码块中。优缺点和上面的是一样的。

    懒汉式(线程不安全)
    class Singleton {
        private static Singleton instance;
        
        private Singleton() {}
        
        //提供一个静态的公有方法,当使用到该方法时,才去创建 instance
        public static Singleton getInstance() {
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    起到了懒加载的效果,但是在多线程环境下不安全,可能会创建多个对象实例出来,因此这个方法是不能使用的

    懒汉式(同步方法,线程安全)
    // 懒汉式(线程安全,同步方法)
    class Singleton {
        private static Singleton instance;
        
        private Singleton() {}
        
        //提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
        public static synchronized Singleton getInstance() {
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    解决了线程安全的问题,但是效率比较低,如果系统中要频繁的获取对象实例,那么这种方法毫无疑问是不推荐的。

    懒汉式(同步代码块,线程不安全)
    class Singleton {
        private static Singleton instance;
        
        private Singleton() {}
        
        //提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
        public static  Singleton getInstance() {
            if(instance == null) {
                synchronized (Singleton.class){
                    instance = new Singleton();
                }
            }
            return instance;
        }
    }
    

    毫无疑问 这是线程不安全的,多线程环境下不能使用这个方法。

    双锁检验
    class Singleton {
        private static volatile Singleton instance;//volatile 抑制指令重排序
        private Singleton() {}
        
        //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
        //同时保证了效率, 推荐使用
        public static Singleton getInstance() {
            if(instance == null) {
                synchronized (Singleton.class) {
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }   
            }
            return instance;
        }
    }
    

    线程安全,实现了懒加载,实际中可以使用这种方式。

    静态内部类
    class Singleton {
        private Singleton() {}//构造器私有化
        
        //写一个静态内部类,该类中有一个静态属性 Singleton
        private static class SingletonInstance {
            private static final Singleton INSTANCE = new Singleton(); 
        }
        
        //提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
        public static synchronized Singleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }
    

    在加载外部类Singleton 时候,内部类SingletonInstance 不会被加载,只是在调用外部类的getInstance()方法时,内部类SingletonInstance才会去加载,这种方法线程安全,实现了延迟加载,并且代码简单,推荐使用这种方法

    枚举
    public class SingletonTest08 {
        public static void main(String[] args) {
            Singleton instance = Singleton.INSTANCE;
            Singleton instance2 = Singleton.INSTANCE;
            System.out.println(instance == instance2);
            
            System.out.println(instance.hashCode());
            System.out.println(instance2.hashCode());
            
            instance.sayOK();
        }
    }
    
    //使用枚举,可以实现单例, 推荐
    enum Singleton {
        INSTANCE; //属性
        public void sayOK() {
            System.out.println("ok~");
        }
    }
    

    线程安全,还能防止反序列化重新创建新的对象,这种方法是Effective Java的作者Josh BLoch提倡的方法。 推荐使用这种方法。

    JDK中使用的设计模式
    JDK中的java.lang.Runtime 类中就用的了饿汉式实现单例模式

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/sfttmctx.html