zoukankan      html  css  js  c++  java
  • Java中几种常见的设计模式--单例设计模式

    一、什么是单例设计模式

    单例设计模式的定义是只有一个类,并且提供一个全局访问点。

    二、适用于那些场景

    一个对象即可完成所有工作,无需大量创建对象消耗资源。比如一个长连接,建立起来就不断的发送数据,如果每一个请求都创建一个链接,资源很快就被消耗殆尽。

    三、有什么特点

    • 只有一个实例
    • 自我实例化
    • 提供一个全局访问点

    四、单例模式的优缺点

     优点:由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。

     缺点:也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难

    五、如何创建

    方法一 饿汉模式

    使用静态常量,在单例类加载的时候就创建了,常见代码如下:

    class Singleton1 {
        //类内部创建实例
        private static Singleton1 instance=new Singleton1();
        
        //构造方法私有化,防止通过new方式 创建私立
        private Singleton1(){}
        
        //对外提供全局同用的唯一调用方法
        public static Singleton1  getInstance(){
            return instance;
        }
        
    }

    这种方式优点缺点都很明显,

    优点:类加载的时候实例化,防止对线程同时访问问题。

    缺点:类加载时候就实例化,如果没有使用就会造成内存空间的浪费。

    方法二懒汉模式

    为了解决饿汉模式对于不使用造成内存空间浪费的缺点,又有人提出了懒汉模式,懒汉模式就是在使用时才创建实例,如果不使用就不会创建。

    public class Singleton2 {
        
        //先声明句柄,但不立即创建实例
        private static Singleton2 instance;
        
        //构造方法私有化,防止外部通过使用new方式创建实例
        private Singleton2(){}
        
        //想外部提供全局共享的唯一实例化接口方法
        public static Singleton2 getInstance(){
            if(instance==null){ 
                instance=new Singleton2();
            }
            return instance;
        }
    
    }

    但这种懒汉模式也存在一个很大的问题,它有多线程问题,在多个线程同时访问的时候并不能保证单例,我们使用多线程去获取单例,发现获取的实例并不唯一。

    public class mytest {
        public static void main(String[] args){
            
            long l1=System.currentTimeMillis();
            ExecutorService es=Executors.newFixedThreadPool(10);
            for(int i=0;i<10;i++){
                    es.execute(new Runnable(){
                        @Override
                        public void run(){
                            Singleton2 s2=Singleton2.getInstance();
                            System.out.println(s2);
                        }
                    });
                }    
            es.shutdown();
            while(!es.isTerminated()){}
            long l2=System.currentTimeMillis();
            System.out.println(l2-l1);
        }
    }

    zc.com.examp.test1.core.Singleton2@1ffffbce
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@1ffffbce
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    5

    这是因为在执行instance==null语句时,此时还没有创建实例,此时满足条件的线程都进入,执行instance=new Singleton2()语句。对于此问题,我们可以使用synchronized关键字,让getInstance()方法只能同时被一个线程访问。

    public class Singleton2 {
        
        //先声明句柄,但不立即创建实例
        private static Singleton2 instance;
        
        //构造方法私有化,防止外部通过使用new方式创建实例
        private Singleton2(){}
        
        //想外部提供全局共享的唯一实例化接口方法
        public static synchronized Singleton2 getInstance(){
            if(instance==null){ 
                instance=new Singleton2();
            }
            return instance;
        }    
    
    }

    测试结果如下:

    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    zc.com.examp.test1.core.Singleton2@49ce13ed
    9

    可以看到通过使用Synchronized关键字确实可以解决多线程同时访问的问题,但同时也降低了执行效率,每次调用getInstance()方法都要涉及锁的操作。其实我们可以对上面的代码进一步优化。

    public class Singleton2 {
        
        //先声明句柄,但不立即创建实例
        private static Singleton2 instance;
        
        //构造方法私有化,防止外部通过使用new方式创建实例
        private Singleton2(){}
        
        //想外部提供全局共享的唯一实例化接口方法
        public static Singleton2 getInstance(){
            if(instance==null){ 
                 //只有在第一次使用的时候构造实例对象,使用synchronized代码块和双重判断避免多线程问题,并且提供效率
                synchronized(Singleton2.class){
                    if(instance==null){
                        instance=new Singleton2();
                    }
                }
                
            }
            return instance;
        }    
    
    }

    测试结果:

    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    zc.com.examp.test1.core.Singleton2@4c75281a
    5

    这种方式不仅解决了线程不安全问题,又提升了执行效率,只有前面的少数线程可能会获取锁,只要实例被创建,后面的线程一般只需第一个if判断就返回了对象,所以这种方式推荐使用。

    方法三内部静态类。

    我们知道饿汉模式缺点就是类在加载时候就创建了实例,容易造成内存资源的浪费,如果在单例类中再创建一个静态内部类,外部类装载的时候静态内部类不会装载,只有使用的时候才会装载,因此达成了懒汉式的效果,实现代码如下:

    class Singleton3 {
        
        //构造方法私有化,防止通过new方式 创建私立
        private Singleton3(){}
        
         //静态内部类,在外部类加载的时候不会加载静态内部类
        private static class SingletonInstance{
            static Singleton3 instance=new Singleton3();
        }
        
        //对外提供全局同用的唯一调用方法
        public static Singleton3  getInstance(){
            //只有在使用到静态内部类的时候才会加载,并且通过类加载机制保证在初始化的时候只有一个实例产生
            return SingletonInstance.instance;
        }
        
    }

    -----------------------------------------------------------------------------------------------------------------

    参考博文:https://www.cnblogs.com/garryfu/p/7976546.html

    参考博文:https://www.cnblogs.com/ye-feng-yu/p/11183075.html

  • 相关阅读:
    【动态规划】01背包问题
    【Huffman&&贪心】Fence Repair(POJ 3253)
    【STL学习】priority_queue
    【贪心算法】特殊的密码锁(openjudge8469)
    【贪心+二分】疯牛
    用类模板封装链表
    Qt的QString和C++string之间的转换
    Qt模态对话框和非模态对话框
    常见的交换变量的三种方法
    整理的经典面试题及各种库函数的自己实现
  • 原文地址:https://www.cnblogs.com/Jerryoned/p/13263750.html
Copyright © 2011-2022 走看看