天天看点

创建型设计模式--单例模式

  采取一定的方法,使程序中的某个类只存在一个实例对象,且该类对外提供一个获取该对象的方法(一般为静态方法)。

(1)饿汉式(2种写法,线程安全)

  静态变量

  静态代码块

(2)懒汉式(3种写法)

  线程不安全

  线程安全,同步方法

  线程安全,同步代码块(不推荐使用)

(3)双重检查(推荐使用)

(4)静态内部类(推荐使用)

(5)枚举(推荐使用)

(1)步骤:

  step1:构造器私有化(防止通过new创建实例对象)

  step2:在类的内部创建实例对象。

  step3:向外暴露一个静态的公共方法用于获取实例对象。

(2)代码实现:

(3)优缺点:

  优点:写法简单,在类装载时完成了实例化,避免线程同步问题。

  缺点:在类装载前完成实例化,没有实现懒加载(Lazy Loading),可能造成内存的浪费(比如从不使用该类时会造成内存的浪费)。

(4)UML图:

创建型设计模式--单例模式

  step2:在类的内部声明实例对象,并在静态代码块中实例化对象。

(3)优缺点同上例 饿汉式单例模式(静态常量版)

  step2:在类的内部声明实例对象。

  step3:向外暴露一个静态的公共方法用于获取实例对象,在调用该方法时,才去创建实例对象。

  优点:实现了懒加载。

  缺点:只能在单线程下使用,比如线程A与线程B并发执行到  if (singleton == null), 此时便会产生多个实例对象。

  step3:向外暴露一个静态的公共方法(给静态方法加个synchronized关键字,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。

  优点:实现了懒加载,解决了线程不安全的问题。

  缺点:效率太低,每个线程想获取实例对象时,均会触发同步方法,此时会等待前一个线程调用结束后才能使用,使效率低。

  step3:向外暴露一个静态的公共方法(在静态方法内部定义一个代码块,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。

  优点:优化了了上例 懒汉式单例模式(线程安全,同步方法) 的效率问题。

  缺点:能满足大部分线程安全的情况,但是若线程A与线程B并发执行到  if (singleton == null),此时同步代码块的作用就不存在了, 会产生多个实例对象。

  step2:在类的内部声明实例对象,并使用volatile关键字(保证可见性,且防止因JVM指令重排使代码执行顺序不对,从而导致代码执行有误)。

  step3:向外暴露一个静态的公共方法(在静态方法内部定义一个代码块,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。在上例 懒汉式单例模式(同步代码块方法,线程不一定安全) 的基础上,给同步代码块内部加个检查处理。

  优点:线程安全,延迟加载,效率高。常用于多线程开发。

  step2:在类的内部定义一个静态内部类(只有被调用时,才会被加载),并在内部类中实例化对象。

  step3:向外暴露一个静态的公共方法,并在方法中调用静态内部类,用于获取实例对象。

  优点:利用JVM的类加载机制,保证了实例化对象时只有一个线程,从而线程安全。在被调用时静态内部类才会被加载并实例化对象,从而实现懒加载,效率高。

(4)UML图:

创建型设计模式--单例模式

  step1:定义一个枚举类型。

  step2:调用即可

  优点:使用枚举类型创建(enum本质是个继承java.lang.Enum类的final class),保证线程安全,且可以防止反序列化重新创建新的对象。

(1)部分源码

(2)可以看到上述代码中采用的是 饿汉式单例模式(静态变量版)。

(1)当频繁创建、销毁某个对象时,可以采用单例模式。

(2)使用单例模式时,需调用相关方法获取实例,而不是通过new。

(3)当创建对象消耗资源过多时,但又经常使用时,可以采用单例模式创建。

继续阅读