美文网首页
设计模式---单例模式及其java实现

设计模式---单例模式及其java实现

作者: 何甜甜在吗 | 来源:发表于2017-12-17 14:32 被阅读0次

    (一)什么是单例模式
    确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实
    一个简单的单例模式例子:Singleton1.java

    public class Singleton1 {
        private static final Singleton1 singleton1 = new Singleton1(); 
        
        //访问权限为private,该方法不能被外面所访问到
        private Singleton1() {
        }
        
        public static Singleton1 getSingleton1 () {
            return singleton1;
        }
    }
    

    构造函数为private,则外界将不能通过new创建对象,只能通过调用getSingleton1()方法获得对象,并且这个对象是原来就创建好了的
    这一种写法是单例模式中的饿汉式模式,在类加载的过程中就会对这个实例进行初始化,缺点就是不是懒加载的,不是在需要的时候才对它进行初始化
    (二)单例模式的优点
    1)只创建了一个实例,节省内存开销
    2)减少了系统的性能开销
    3)避免对资源的多重占用
    4)在系统设置全局的访问点,优化和共享资源优化
    (三)单例模式的缺点
    1)不易于扩展,单例模式没有接口,不过接口单例模式好像是没什么意义的,单例模式的应用场景是生成对象啊,和接口扯不上边,而且接口是不能实例化的
    2)对测试不利,因为不能使用mock的方式虚拟一个对象
    (四)单例模式的应用场景
    spring ioc使用单利模式创建bean,只是知道它是这么做的,因为没有研读spring源码,所以很多细节需要以后来补充
    (五)并发条件下的单例模式
    单例模式有很多种实现方式
    上面只是其中一种,还有其他比如Singleton2.java

    public class Singleton2 {
        private static Singleton2 singleton2 = null;
        
        private Singleton2() {}
        
        public static Singleton2 getSingleton2() {
            if (singleton2 == null) {
                singleton2 = new Singleton2();
            }
            
            return singleton2;
        }
    }
    

    Singleton1在并发的情况下是线程安全的,但Singleton2在并发情况下是线程不安全的,很有可能创建多个实例
    修改方式:
    1)在方法前面加上synchronized关键字,或者使用Lock来实现

     public static synchronized Singleton2 getSingleton2() {
            if (singleton2 == null) {
                singleton2 = new Singleton2();
            }
    
            return singleton2;
        }
    

    2)同步代码块

     public static Singleton2 getSingleton2() {
            synchronized(singleton2) {
                if (singleton2 == null) {
                    singleton2 = new Singleton2();
                }
            }
            return singleton2;
        }
    

    目前比较熟悉的解决并发创建对象的方法,以后还要继续补充
    上面两种虽然解决了线程安全,但并不高效
    3)双重检查锁

    public class Singleton3 {
        private static volatile Singleton3 singleton3 = null;
    
        private Singleton3() {}
    
        public static Singleton3 getSingleton2() {
            if (singleton3 == null) {
                synchronized (Singleton3.class) {
                    if (singleton3 == null) {
                        singleton3 = new Singleton3();
                    }
                }
            }
    
            return singleton3;
        }
    }
    

    为什么需要对singleton3添加volatile修饰符,因为singleton3 = new Singleton3();不是原子性的,分为三步
    1.为singleton3分配内存
    2.调用构造函数进行初始化
    3.将singleton3对象指向分配的内存(执行完这步singleton3将不为null)
    为了提高程序的运行效率,编译器会进行一个指令重排,线程1执行到= new Singleton3();线程2执行到if (singleton3 == null)假设进行了指令重排,2和3顺序相反,此时singleton3不为null,则线程2此时可能直接返回未正确进行初始化的singleton3对象。使用volatile可以禁止指令重排序
    4)静态内部类

    public class Singleton4 {
        private static class SingletonHolder {
            private static final Singleton4 singleton4 = new Singleton4();
        }
        
        private Singleton4() {}
        
        public static Singleton4 getSingleton4() {
            return SingletonHolder.singleton4;
        }
    }
    

    因为被static修饰,所以只会初始化一次,并且singleton4只会在访问该字段时初始化一次,所以它是懒汉式的
    5)枚举方式

    public enum Singleton5 {
        INSTANCE;
    
        public void hello() {
            System.out.println("hello, successful");
        }
    }
    
    public class Singleton5Test {
        public static void main(String[] args) {
            Singleton5 singleton5 = Singleton5.INSTANCE;
            singleton5.hello();
        }
    }
    

    代码相当优美
    实现原理:枚举类的域(field)其实是相应的enum类型的一个实例对象
    可以参考implementing-singleton-with-an-enum-in-java
    (六)单例模式的扩展---多例模式
    产生固定数量对象的模式。通过一个计数器实现

    感觉自己的积累量还不够,很多东西都没办法想明白,比如这种设计模式的优缺点,为什么是这样的吗,如何解决,还有很长的路要走,厚积而薄发,切勿浮躁!!!

    相关文章

      网友评论

          本文标题:设计模式---单例模式及其java实现

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