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

一、单例模式

1、什么是单例模式

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

2、单例模式分类

(1)饿汉式(2种写法,线程安全)
  静态变量
  静态代码块

(2)懒汉式(3种写法)
  线程不安全
  线程安全,同步方法
  线程安全,同步代码块(不推荐使用)

(3)双重检查(推荐使用)
(4)静态内部类(推荐使用)
(5)枚举(推荐使用)

3、饿汉式单例模式(静态常量版)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部创建实例对象。
  step3:向外暴露一个静态的公共方法用于获取实例对象。
(2)代码实现:

package singleton.pattern.demo1;

/**
 * 演示 饿汉式单例模式,静态变量版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 饿汉式单例模式,静态变量版
 */
class Singleton {
    // 在类的内部创建实例对象。使用静态变量,只被加载一次。
    private static Singleton singleton = new Singleton();

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        return singleton;
    }
}

(3)优缺点:
  优点:写法简单,在类装载时完成了实例化,避免线程同步问题。
  缺点:在类装载前完成实例化,没有实现懒加载(Lazy Loading),可能造成内存的浪费(比如从不使用该类时会造成内存的浪费)。

(4)UML图:

4、饿汉式单例模式(静态代码块)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部声明实例对象,并在静态代码块中实例化对象。
  step3:向外暴露一个静态的公共方法用于获取实例对象。
(2)代码实现:

package singleton.pattern.demo2;

/**
 * 演示 饿汉式单例模式,静态代码块版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 饿汉式单例模式,静态代码块版
 */
class Singleton {
    // 在类的内部声明一个实例对象
    private static Singleton singleton;

    static {
        // 在代码块中实例化一个对象,同样只加载一次
        singleton = new Singleton();
    }

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        return singleton;
    }
}

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

5、懒汉式单例模式(线程不安全)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部声明实例对象。
  step3:向外暴露一个静态的公共方法用于获取实例对象,在调用该方法时,才去创建实例对象。
(2)代码实现:

package singleton.pattern.demo3;

/**
 * 演示 懒汉式单例模式,线程不安全版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 懒汉式单例模式,线程不安全版
 */
class Singleton {
    // 在类的内部声明一个实例对象
    private static Singleton singleton;

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象。 当调用该方法时,才去检查并创建一个实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

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

6、懒汉式单例模式(线程安全,同步方法)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部声明实例对象。
  step3:向外暴露一个静态的公共方法(给静态方法加个synchronized关键字,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。
(2)代码实现:

package singleton.pattern.demo4;

/**
 * 演示 懒汉式单例模式,线程安全,同步方法版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 懒汉式单例模式,线程安全,同步方法版
 */
class Singleton {
    // 在类的内部声明一个实例对象
    private static Singleton singleton;

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象,并给方法加个synchronized关键字,解决同步的问题。
     * 当调用该方法时,才去检查并创建一个实例对象。
     *
     * @return 实例对象
     */
    public static synchronized Singleton getSingleTon() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

(3)优缺点:
  优点:实现了懒加载,解决了线程不安全的问题。
  缺点:效率太低,每个线程想获取实例对象时,均会触发同步方法,此时会等待前一个线程调用结束后才能使用,使效率低。

7、懒汉式单例模式(同步代码块方法,线程不一定安全)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部声明实例对象。
  step3:向外暴露一个静态的公共方法(在静态方法内部定义一个代码块,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。
(2)代码实现:

package singleton.pattern.demo5;

/**
 * 演示 懒汉式单例模式,线程不一定安全,同步代码块版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 懒汉式单例模式,线程不一定安全,同步代码块版
 */
class Singleton {
    // 在类的内部声明一个实例对象
    private static Singleton singleton;

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象,在方法内部加个同步代码块。 当调用该方法时,才去检查并创建一个实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

(3)优缺点:
  优点:优化了了上例 懒汉式单例模式(线程安全,同步方法) 的效率问题。
  缺点:能满足大部分线程安全的情况,但是若线程A与线程B并发执行到 if (singleton == null),此时同步代码块的作用就不存在了, 会产生多个实例对象。

8、双重检查(Double Check)

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部声明实例对象,并使用volatile关键字(保证可见性,且防止因JVM指令重排使代码执行顺序不对,从而导致代码执行有误)。
  step3:向外暴露一个静态的公共方法(在静态方法内部定义一个代码块,解决同步问题)用于获取实例对象,在调用该方法时,才去创建实例对象。在上例 懒汉式单例模式(同步代码块方法,线程不一定安全) 的基础上,给同步代码块内部加个检查处理。
(2)代码实现:

package singleton.pattern.demo6;

/**
 * 演示 单例模式,双重检查版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 单例模式,双重检查版
 */
class Singleton {
    // 在类的内部声明一个实例对象
    private static volatile Singleton singleton;

    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象,在方法内部加个同步代码块,在代码块内部增加一个检查处理。 当调用该方法时,才去检查并创建一个实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

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

9、静态内部类

(1)步骤:
  step1:构造器私有化(防止通过new创建实例对象)
  step2:在类的内部定义一个静态内部类(只有被调用时,才会被加载),并在内部类中实例化对象。
  step3:向外暴露一个静态的公共方法,并在方法中调用静态内部类,用于获取实例对象。
(2)代码实现:

package singleton.pattern.demo7;

/**
 * 演示 单例模式,静态内部类版
 *
 */
public class Demo {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getSingleTon();
        Singleton singleton2 = Singleton.getSingleTon();
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 单例模式,静态内部类版
 */
class Singleton {
    /**
     * 构造器私有化(防止通过new创建实例对象)
     */
    private Singleton() {
    }

    /**
     * 静态内部类,在被调用的时候才会被加载,实现懒加载。 且内部使用静态常量实例化一个对象,保证了线程安全问题。
     */
    public static class SingleTonInstance {
        public static final Singleton INSTANCE = new Singleton();
    }

    /**
     * 向外暴露一个静态的公共方法用于获取实例对象,在方法内部加个同步代码块,在代码块内部增加一个检查处理。 当调用该方法时,才去检查并创建一个实例对象。
     *
     * @return 实例对象
     */
    public static Singleton getSingleTon() {
        return SingleTonInstance.INSTANCE; // 调用静态内部类的静态属性
    }
}

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

(4)UML图:

10、枚举

(1)步骤:
  step1:定义一个枚举类型。
  step2:调用即可
(2)代码实现:

package singleton.pattern;

/**
 * 演示 单例模式,枚举版
 *
 */
public class Demo {
    public static void main(String[] args) {
        SingleTon singleton1 = SingleTon.INSTACNE;
        SingleTon singleton2 = SingleTon.INSTACNE;
        System.out.println(singleton1 == singleton2); // 由于获取的为同一个对象,所以输出为true
    }
}

/**
 * 单例模式,枚举版
 */
enum SingleTon {
    INSTACNE;
    public void show() {
        System.out.println("hello world");
    }
}

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

11、JDK中的单例模式举例(Runtime)

(1)部分源码

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
}

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

12、单例模式使用注意

(1)当频繁创建、销毁某个对象时,可以采用单例模式。
(2)使用单例模式时,需调用相关方法获取实例,而不是通过new。
(3)当创建对象消耗资源过多时,但又经常使用时,可以采用单例模式创建。

(0)

相关推荐

  • 单例模式的实现

    记一下学习单例模式的笔记: 单例就是要保证该类仅有一个实例.实现完全封闭的单例(外部不能new)其实就要两点要求: 全局访问:需要一个该类型的全局静态变量,每次获取实例时都要判断它是否null,不存在 ...

  • 设计模式(一)——Java单例模式(代码+源码分析)

    设计模式(一)——Java单例模式(代码+源码分析)

  • 深入灵魂的考验,每行注释都是灵魂的单例模式,源码+实例降临

    不管是设计模式也好,别的模式也要,他都是为了解决问题而发明的有效的方法.除了我们已经熟悉的23种设计模式以外,还有MVVM.Combinator等其它的东西,都已经是前辈们经过多年的摸爬滚打总结出来的 ...

  • 23种设计模式入门 -- 单例模式

    单例模式:采用一定的方法,使得软件运行中,对于某个类只能存在一个实例对象,并且该类只能提供一个取得实例的方法. 分类: 饿汉式 静态常量方式 静态代码块方式 懒汉式 普通方式,线程不安全 同步方法方式 ...

  • SingletonPattern单例模式

    单例模式 1.定义 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 饿汉式单例模式 public class Singleton { private static final Sin ...

  • 单例模式

    什么是Singleton? 单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式. 例如:代表JVM运行环境的Runtime类. 要点 一是某个类只能有一个实例 构造器私有化 ...

  • 面试高频-吃透单例设计模式

    单例设计模式 单例设计模式的介绍 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法). 比如 Hiber ...

  • 设计模式(2) 单例模式

    单例模式 线程安全的Singleton 会破坏Singleton的情况 线程级Singleton 单例模式是几个创建型模式中最独立的一个,它的主要目标不是根据客户程序调用生成一个新的实例,而是控制某个 ...

  • 面向对象23种设计模式系列(一)- 创建型设计模式

    本章是面向对象23种设计模式系列开篇,首先我们来看下什么是设计模式? 面向对象23种设计模式: 1.面向对象语言开发过程中,遇到的种种场景和问题,提出了解决方案和思路,沉淀下来就变成了设计模式. 2. ...

  • 创建型设计模式总结

    Intro 前面几篇文章已经把创建型设计模式都介绍了,来做一个简单的总结. 创建型设计模式,就是用来创建对象的设计模式,根据要创建的对象的复杂度以及是否允许多实例以及是否需要容易扩展等多方面考虑去选择 ...

  • 创建型设计模式--工厂模式、抽象工厂模式

    一.小案例分析 1.功能需求: 实现一个发送信息的功能,要便于扩展与维护. (1)发送信息的工具有很多,比如短信.微信.邮件.QQ等. (2)选择某个工具进行信息发送. 2.小菜鸡去实现: (1)定义 ...

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

    前言: 单例模式,顾名思义,只存在一个实例.官方定义:对于类的单例模式设计,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法). ...

  • 创建型模式之单例模式

    定义与特点 单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式. 例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成 ...

  • 设计模式-创建型-建造者模式

    引言: 无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮.底盘.发动机.方向盘等各种部件.而对于大部分用户而言,无须知道这些部件的装配细节,也几乎不会 ...

  • 设计模式-创建型-抽象工厂模式

    前一章节,我们介绍了简单工厂模式以及工厂方法模式,但是这两种模式都存在一定的局限性,只能生产某一类型下的某一种产品,如果需求变更,同类型下出现了不同的产品,比如芝士披萨不仅有口味上的不同,同时存在外观 ...

  • 设计模式-创建型-工厂模式

    工厂设计模式: 顾名思义,该模式是用来生产对象的.在面向对象的设计模式中,万物皆对象,若使用new来创建对象,就会对该对象产生强耦合,假如我们需要更换该对象,那么使用该对象的对象都需要进行修改,这显然 ...

  • 设计模式-单例模式

    单例模式 优点 由于单例模式在内存中只有一个实例,减少了内存开支,特别是 一个对象需要频繁地创建.销毁时,而且创建或销毁时性能又无法优 化,单例模式的优势就非常明显. 由于单例模式只生成一个实例,所以 ...