单例模式_单例模式详解 - CSDN
单例模式 订阅
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例) 展开全文
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
信息
外文名
Singleton pattern
类    别
设计模式
中文名
单例模式
解    释
常用的软件设计模式
单例模式定义
数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”Java单例模式例子
收起全文
精华内容
参与话题
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2019-07-27 08:07:21
    本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...

    本文继续介绍23种设计模式系列之单例模式。

    概念:
      java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
      单例模式有以下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。
      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


    一、懒汉式单例

     

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    }

     

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:

     

    1、在getInstance方法上加同步

     

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }

     

     

     

    2、双重检查锁定

     

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

     

    3、静态内部类

     

    public class Singleton {  
        private static class LazyHolder {  
           private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
           return LazyHolder.INSTANCE;  
        }  
    }  

    这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

     

     

     

     

     

     

    二、饿汉式单例

     

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

     

     

     

     

    三、登记式单例(可忽略)

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。
    public class Singleton3 {
        private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
        static{
            Singleton3 single = new Singleton3();
            map.put(single.getClass().getName(), single);
        }
        //保护的默认构造子
        protected Singleton3(){}
        //静态工厂方法,返还此类惟一的实例
        public static Singleton3 getInstance(String name) {
            if(name == null) {
                name = Singleton3.class.getName();
                System.out.println("name == null"+"--->name="+name);
            }
            if(map.get(name) == null) {
                try {
                    map.put(name, (Singleton3) Class.forName(name).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return map.get(name);
        }
        //一个示意性的商业方法
        public String about() {    
            return "Hello, I am RegSingleton.";    
        }    
        public static void main(String[] args) {
            Singleton3 single3 = Singleton3.getInstance(null);
            System.out.println(single3.about());
        }
    }

     登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

    这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

     

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。


     

    2、资源加载和性能:

    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     

    应用

    以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

     

    public class TestSingleton {
    	String name = null;
    
            private TestSingleton() {
    	}
    
    	private static volatile TestSingleton instance = null;
    
    	public static TestSingleton getInstance() {
               if (instance == null) {  
                 synchronized (TestSingleton.class) {  
                    if (instance == null) {  
                       instance = new TestSingleton(); 
                    }  
                 }  
               } 
               return instance;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void printInfo() {
    		System.out.println("the name is " + name);
    	}
    
    }

    可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

    还有疑问可参考http://www.iteye.com/topic/652440
    和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

     

     

     

    public class TMain {
    	public static void main(String[] args){
    		TestStream ts1 = TestSingleton.getInstance();
    		ts1.setName("jason");
    		TestStream ts2 = TestSingleton.getInstance();
    		ts2.setName("0539");
    		
    		ts1.printInfo();
    		ts2.printInfo();
    		
    		if(ts1 == ts2){
    			System.out.println("创建的是同一个实例");
    		}else{
    			System.out.println("创建的不是同一个实例");
    		}
    	}
    }
    

     运行结果:

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

    对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

    更多设计模式:23种设计模式系列

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

    展开全文
  • 什么是单例模式

    万次阅读 多人点赞 2018-01-24 17:29:47
    单例模式第一版: public class Singleton { private Singleton() {} //私有构造函数 private static Singleton instance = null; //单例对象 //静态工厂方法 public static Singleton getInstance() { if ...
    单例模式第一版:
    
    public class Singleton {
        private Singleton() {}  //私有构造函数
        private static Singleton instance = null;  //单例对象
        //静态工厂方法
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

    为什么这样写呢?我们来解释几个关键点:


    1.要想让一个类只能构建一个对象,自然不能让它随便去做new操作,因此Signleton的构造方法是私有的。


    2.instance是Singleton类的静态成员,也是我们的单例对象。它的初始值可以写成Null,也可以写成new Singleton()。至于其中的区别后来会做解释。


    3.getInstance是获取单例对象的方法。


    如果单例初始值是null,还未构建,则构建单例对象并返回。这个写法属于单例模式当中的懒汉模式。


    如果单例对象一开始就被new Singleton()主动构建,则不再需要判空操作,这种写法属于饿汉模式


    这两个名字很形象:饿汉主动找食物吃,懒汉躺在地上等着人喂。但是上边单列不是线程安全的单列!


    *****

    为什么说刚才的代码不是线程安全呢?


    假设Singleton类刚刚被初始化,instance对象还是空,这时候两个线程同时访问getInstance方法:


    因为Instance是空,所以两个线程同时通过了条件判断,开始执行new操作:


    这样一来,显然instance被构建了两次。让我们对代码做一下修改:

    单例模式第二版:
    
    public class Singleton {
        private Singleton() {}  //私有构造函数
       private static Singleton instance = null;  //单例对象
       //静态工厂方法
       public static Singleton getInstance() {
            if (instance == null) {      //双重检测机制
             synchronized (Singleton.class){  //同步锁
               if (instance == null) {     //双重检测机制
                 instance = new Singleton();
                   }
                }
             }
            return instance;
        }
    }
    
    为什么这样写呢?我们来解释几个关键点:
    
    1.为了防止new Singleton被执行多次,因此在new操作之前加上Synchronized 同步锁,锁住整个类(注意,这里不能使用对象锁)。
    
    2.进入Synchronized 临界区以后,还要再做一次判空。因为当两个线程同时访问的时候,线程A构建完对象,线程B也已经通过了最初的判空验证,不做第二次判空的话,线程B还是会再次构建instance对象。









    像这样两次判空的机制叫做双重检测机制。总体上可以,但是这段代码仍然不是绝对的线程安全!

    假设这样的场景,当两个线程一先一后访问getInstance方法的时候,当A线程正在构建对象,B线程刚刚进入方法


    这种情况表面看似没什么问题,要么Instance还没被线程A构建,线程B执行 if(instance == null)的时候得到true;要么Instance已经被线程A构建完成,线程B执行 if(instance == null)的时候得到false。


    真的如此吗?答案是否定的。这里涉及到了JVM编译器的指令重排


    指令重排是什么意思呢?比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:


    memory =allocate();    //1:分配对象的内存空间 

    ctorInstance(memory);  //2:初始化对象 

    instance =memory;     //3:设置instance指向刚分配的内存地址 


    但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:


    memory =allocate();    //1:分配对象的内存空间 

    instance =memory;     //3:设置instance指向刚分配的内存地址 

    ctorInstance(memory);  //2:初始化对象 


    当线程A执行完1,3,时,instance对象还未完成初始化,但已经不再指向null。此时如果线程B抢占到CPU资源,执行  if(instance == null)的结果会是false,从而返回一个没有初始化完成的instance对象。如下图所示:






    如何避免这一情况呢?我们需要在instance对象前面增加一个修饰符volatile。
    
    
    单例模式第三版:
    
    public class Singleton {
        private Singleton() {}  //私有构造函数
        private volatile static Singleton instance = null;  //单例对象
        //静态工厂方法
        public static Singleton getInstance() {
              if (instance == null) {      //双重检测机制
             synchronized (Singleton.class){  //同步锁
               if (instance == null) {     //双重检测机制
                 instance = new Singleton();
                    }
                 }
              }
              return instance;
          }
    }
    用最简单的方式理解,volatile修饰符阻止了变量访问前后的指令重排,保证了指令执行顺序!

    经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序:


    memory =allocate();    //1:分配对象的内存空间 

    ctorInstance(memory);  //2:初始化对象 

    instance =memory;     //3:设置instance指向刚分配的内存地址 


    如此在线程B看来,instance对象的引用要么指向null,要么指向一个初始化完毕的Instance,而不会出现某个中间态,保证了安全。


    二,实现单列模式的手段非常多,我们先看一看通过静态内部类实现的单列模式。


    用静态内部类实现单例模式:
    
    public class Singleton {
        private static class LazyHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        private Singleton (){}
        public static Singleton getInstance() {
            return LazyHolder.INSTANCE;
        }
    }
    
    这里有几个需要注意的点:
    
    1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。
    
    2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。
    静态内部类的实现方式虽好,但是也存在着单例模式共同的问题,无法防止利用反射来重复构建对象。
    我们先来看怎样通过反射来打破单例模式只能构建一个对象的实例约束!

    利用反射打破单例:
    
    //获得构造器
    Constructor con = Singleton.class.getDeclaredConstructor();
    //设置为可访问
    con.setAccessible(true);
    //构造两个不同的对象
    Singleton singleton1 = (Singleton)con.newInstance();
    Singleton singleton2 = (Singleton)con.newInstance();
    //验证是否是不同对象
    System.out.println(singleton1.equals(singleton2));
    
    
    代码可以简单归纳为三个步骤:
    
    第一步,获得单例类的构造器。
    
    第二步,把构造器设置为可访问。
    
    第三步,使用newInstance方法构造对象。
    
    最后为了确认这两个对象是否真的是不同的对象,我们使用equals方法进行比较。毫无疑问,比较结果是false。

    我们可以使用枚举来实现单例模式,这又是一种优雅而又简洁的方式

    用枚举实现单例模式:
    
    public enum SingletonEnum {
        INSTANCE;
    }
    有了enum,JVM会阻止反射获取枚举的私有构造方法。让我们来做一个实验,仍然执行刚才的反射代码:
    //获得构造器
    Constructor con = SingletonEnum.class.getDeclaredConstructor();
    //设置为可访问
    con.setAccessible(true);
    //构造两个不同的对象
    SingletonEnum singleton1 = (SingletonEnum)con.newInstance();
    SingletonEnum singleton2 = (SingletonEnum)con.newInstance();
    //验证是否是不同对象
    System.out.println(singleton1.equals(singleton2));
    
    
    执行获得构造器这一步的时候,抛出了如下异常:
    
    Exception in thread "main" java.lang.NoSuchMethodException: com.xiaohui.singleton.test.SingletonEnum.<init>()
    	at java.lang.Class.getConstructor0(Class.java:2892)
    	at java.lang.Class.getDeclaredConstructor(Class.java:2058)
    	at com.xiaohui.singleton.test.SingletonTest.main(SingletonTest.java:22)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
    使用枚举实现的单例模式不仅能够防止反射构造对象,而且可以保证线程安全。不过这种方式也有唯一缺点,就是他并非使用懒加载,其单例对象实在枚举类被加载的时候进行初始化的。基本上单列模式介绍到这里!下面做一个简单总结。




    几点补充:


    1. volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。有关volatile的详细原理,我在以后的漫画中会专门讲解。


    2.使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。


    对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。



    展开全文
  • 单例模式

    千次阅读 2019-03-22 16:07:59
    单例的创建方式: 1、饿汉式:类初始化的时候,会立即加载该对象,线程天生安全,调用效率高。...4、枚举单例:使用枚举实现单例模式,实现简单、调用效率高,枚举本身就是单例,由JVM从根本上提供保障,...

    单例的创建方式:

    1、饿汉式:类初始化的时候,会立即加载该对象,线程天生安全,调用效率高。

    2、懒汉式:类初始化时,不会初始化该对象,真正需要使用的时候才会去创建该对象,具备懒加载功能。

    3、静态内部类方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。

    4、枚举单例:使用枚举实现单例模式,实现简单、调用效率高,枚举本身就是单例,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞,缺点是没有延迟加载。

    5、双重检测方式(因为JVM本身重排序的原因,可能会出现多次的初始化)

     

     

    示例:

    1、饿汉式

    //饿汉式
    public class SingletonDemo01 {
    	// 类初始化时,会立即加载该对象,线程天生安全,调用效率高
    	private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
    
    	private SingletonDemo01() {
    		System.out.println("SingletonDemo01初始化");
    	}
    
    	public static SingletonDemo01 getInstance() {
    		System.out.println("getInstance");
    		return singletonDemo01;
    	}
    
    	public static void main(String[] args) {
    		SingletonDemo01 s1 = SingletonDemo01.getInstance();
    		SingletonDemo01 s2 = SingletonDemo01.getInstance();
    		System.out.println(s1 == s2);
    	}
    
    }

    2、懒汉式

    //懒汉式
    public class SingletonDemo02 {
    
    	//类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。
    	private static SingletonDemo02 singletonDemo02;
    
    	private SingletonDemo02() {
       
    	}
    
    	public synchronized static SingletonDemo02 getInstance() {
    		if (singletonDemo02 == null) {
    			singletonDemo02 = new SingletonDemo02();
    		}
    		return singletonDemo02;
    	}
    
    	public static void main(String[] args) {
    		SingletonDemo02 s1 = SingletonDemo02.getInstance();
    		SingletonDemo02 s2 = SingletonDemo02.getInstance();
    		System.out.println(s1 == s2);
    	}
    
    }

    3、静态内部类

    public class SingletonDemo03 {
    	private SingletonDemo03() {
               System.out.println("初始化..");
    	}
    
    	public static class SingletonClassInstance {
    		private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03();
    	}
    
    	// 方法没有同步
    	public static SingletonDemo03 getInstance() {
    		System.out.println("getInstance");
    		return SingletonClassInstance.singletonDemo03;
    	}
    
    	public static void main(String[] args) {
    		SingletonDemo03 s1 = SingletonDemo03.getInstance();
    		SingletonDemo03 s2 = SingletonDemo03.getInstance();
    		System.out.println(s1 == s2);
    	}
    }

    优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

    劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。

    4、枚举

    public class User {
    	public static User getInstance() {
    		return SingletonDemo04.INSTANCE.getInstance();
    	}
    
    	private static enum SingletonDemo04 {
    		INSTANCE;
    		// 枚举元素为单例
    		private User user;
    
    		private SingletonDemo04() {
    			.out.println("SingletonDemo04");
    			user = new User();
    		}
    
    		public User getInstance() {
    			return user;
    		}
    	}
    
    	public static void main(String[] args) {
    		User u1 = User.getInstance();
    		User u2 = User.getInstance();
    		System.out.println(u1 == u2);
    	}
    }

    5、双重检测锁

    public class SingletonDemo04 {
    	private SingletonDemo04 singletonDemo04;
    
    	private SingletonDemo04() {
    
    	}
    
    	public SingletonDemo04 getInstance() {
    		if (singletonDemo04 == null) {
    			synchronized (this) {
    				if (singletonDemo04 == null) {
    					singletonDemo04 = new SingletonDemo04();
    				}
    			}
    		}
    		return singletonDemo04;
    	}
    
    }

     

    单例防止反射攻击

    在构造函数中,只能允许初始化一次即可

        private static boolean flag = false;
    
    	private SingletonDemo04() {
    
    		if (flag == false) {
    			flag = !flag;
    		} else {
    			throw new RuntimeException("单例模式被侵犯!");
    		}
    	}

     

    展开全文
  • 单例模式的五种写法

    万次阅读 多人点赞 2019-06-23 13:52:48
    其中接下来我们要写的是单例模式,属于创建型模式。 单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面...

    设计模式(Design pattern),提供了在软件开发过程中面临的一些问题的最佳解决方案,是Java开发者必修的一门课程。主要分创建型模式、结构型模式和行为型模式。其中接下来我们要写的是单例模式,属于创建型模式。

    单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面我们来看下有哪几种实现方式吧。

    核心代码:构造方法私有化,private。

    1、懒汉式

    懒汉式,顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。

    2、饿汉式

    饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。

    3、双检锁

    双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。

    4、静态内部类

    静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

    5、枚举

    枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。

    好了,上面就是单例模式的五种主要写法。我们来总结下,一般情况下,懒汉式(包含线程安全和线程不安全梁总方式)都比较少用;饿汉式和双检锁都可以使用,可根据具体情况自主选择;在要明确实现 lazy loading 效果时,可以考虑静态内部类的实现方式;若涉及到反序列化创建对象时,大家也可以尝试使用枚举方式。

    展开全文
  • 深入理解单例模式——只有一个实例

    万次阅读 多人点赞 2019-04-16 10:44:10
    初遇设计模式在上个寒假,当时把每个设计模式过了一遍,对设计模式有了一个最初级的了解。这个学期借了几本设计模式的书籍看,听了老师的设计模式课,对设计模式算是有个更进一步的认识。后面可能会不定期更新一下...
  • 单例模式6种实现方式

    千次阅读 2018-06-04 17:21:36
    // 立即加载方式==饿汉模式 private static MyObject myObject = new MyObject(); private MyObject() { } public static MyObject getInstance() { // 此代码版本为立即加载 // 此版本代码的缺点是不能...
  • 单例模式和多例模式的总结

    万次阅读 多人点赞 2019-07-27 18:26:48
    单例模式的关键有两点: 1.构造方法为私有,这样外界就不能随意调用。 2.get的方法为静态,由类直接调用 多例模式(Multiton) 1、多例类可以有多个实例 2、多例类必须能够自我创建并管理自己的实例,并向外界...
  • Java与单例模式

    千次阅读 2019-02-24 20:08:13
    让我对这个单例模式(原本我以为是设计模式中最简单的一种)有了更深的认识。 单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建...
  • 什么是单例模式

    千次阅读 多人点赞 2019-05-13 16:51:29
    什么是单例模式? 保证整个系统中一个类只有一个对象的实例,实现这种功能的方式就叫单例模式。 为什么要用单例模式? 1、单例模式节省公共资源 比如:大家都要喝水,但是没必要每人家里都打一口井是吧,通常的...
  • C++的三种单例模式-----深度解析

    万次阅读 多人点赞 2020-09-09 16:53:40
    小编想要对三种的单例模式做下解析 简介 因为在设计或开发中,肯定会有这么一种情况,一个类只能有一个对象被创建,如果有多个对象的话,可能会导致状态的混乱和不一致。这种情况下,单例模式是最恰当的解决...
  • java单例模式

    万次阅读 多人点赞 2020-03-09 15:47:46
     本文首先概述了单例模式产生动机,揭示了单例模式的本质和应用场景。紧接着,我们给出了单例模式在单线程环境下的两种经典实现:饿汉式和懒汉式,但是饿汉式是线程安全的,而懒汉式是非线程安全的。在多线程环境下...
  • 单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保...
  • Java实现单例模式的9种方法

    万次阅读 多人点赞 2019-12-22 14:19:05
    因进程需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。 二. 单例模式的特点 1、单例模式只能有一个实例。 2、单例类必须创建自己的唯一实例。 3、单例类...
  • 单例模式的使用总结

    万次阅读 2020-04-21 11:20:12
    一、单例模式的定义和应用场景 (一)定义及基本要点 (二)应用场景 二、饿汉式单例模式 (一)基本代码展示分析 (二)基本分析和建议 三、懒汉式单例模式(双重检查锁) (一)基本代码展示分析 (二)...
  • 单例模式,参考完整代码在GitHub 地址:https://github.com/zhang-xiaoxiang/patter23 或者https://github.com/zhang-xiaoxiang/DesignPatterns23 一般三步走,1初始化2构造器私有3提供获取实例的方法 1单例模式---...
  • 单例模式讨论篇:单例模式与垃圾回收

    万次阅读 多人点赞 2012-03-10 01:44:57
    Jvm的垃圾回收机制到底会不会回收掉长时间不用的单例模式对象,这的确是一个比较有争议性的问题。将这一部分内容单独成篇的目的也是为了与广大博友广泛的讨论一下这个问题。为了能让更多的人看到这篇文章,请各位...
  • java 单例模式的几种实现方式

    万次阅读 多人点赞 2020-01-02 12:59:57
    1.懒汉式 public class Singleton { //2.本类内部创建对象实例 private static Singleton instance = null; /** * 1.构造方法私有化,外部不能new */ ...//3.... public static Single...
  • 深入理解单例模式:静态内部类单例原理

    万次阅读 多人点赞 2019-03-18 10:57:48
    本文主要介绍java的单例模式,以及详细剖析静态内部类之所以能够实现单例的原理。OK,废话不多说,进入正文。 首先我们要先了解下单例的四大原则: 1.构造私有。 2.以静态方法或者枚举返回实例。 3.确保实例只有...
  • 设计模式(二)单例模式的七种写法

    万次阅读 多人点赞 2019-05-21 18:16:49
    面试的时候,问到许多年轻的Android开发他所会的设计模式是什么,基本上都会提到单例模式,但是对单例模式也是一知半解,在Android开发中我们经常会运用单例模式,所以我们还是要更了解单例模式才对。 **定义:保证...
  • C++中的单例模式

    万次阅读 多人点赞 2013-09-17 17:18:14
    单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,...
1 2 3 4 5 ... 20
收藏数 254,263
精华内容 101,705
关键字:

单例模式