美文网首页
单例模式

单例模式

作者: 潔雲 | 来源:发表于2020-07-27 23:45 被阅读0次

Singleton模式 只有一个实例

1. Singleton模式

Singleton模式的目的:

  • 想确保任何情况下都绝对只有1个实例
  • 想在程序上表现出“只存在一个实例”

2. 示例程序

2.1 Singleton类

public class Singleton{
    private static Singleton singleton = new Singleton();
    private Singleton(){
        System.out.println("生成了一个实例。");
    }
    public static Singleton getInstance(){
            return singleton;
    }
}

Singleton类的构造函数是private的,这是为了禁止从Singleton类外部调用构造函数。如果从Singleton类意外的代码中调用构造函数new Singleton(),就会出现编译错误。如果程序员十分小心,不会使用new关键字生成实例,就不需要定义构造函数为private,但是这样的话,这个模式也没有意义了。 Singleton模式的作用在于可以确保在任何情况下都只能生成一个实例。

2.2 Main类

public class Main {
    public static void main(String[] args) {
        System.out.println("Start.");
        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();
        if (obj1 == obj2){
            System.out.println("obj1和obj2是相同的实例");
        }else {
            System.out.println("obj1和obj2是不同的实例");
        }
        System.out.println("End");
    }
}

运行结果:


result.png

3. 拓展思路

3.1 为什么必须设置限制

设置限制其实就是为程序增加一项前提条件。档存在多个实例时,可能会产生意想不到的Bug。但是如果我们可以确保只有一个实例,就可以在这个前提条件下放心的编程了。

3.2 何时生成这个唯一的实例

在程序运行后,在第一次调用getInstance方法时,Singleton类会被初始化。也就是在这个时候,static字段singleton被初始化,生成了唯一的实例。

3.3 习题

某位开发人员编写了如下的Singleton类,但是这并非严格的Singleton模式。请问为什么?

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
        System.out.println("生成了一个实例");
    }
    public static Singleton getInstance(){
        if (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

答:这是因为在国歌线程几乎同时调用Singleton.getInstance方法时,可能会生成多个实例。
例:
Singleton.java

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
        System.out.println("生成了一个实例");
        slowdown();
    }
    public static Singleton getInstance(){
        if (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }

    private void slowdown(){
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){

        }
    }
}

Main.java

public class Main extends Thread{
    public static void main(String[] args) {
        System.out.println("Start.");
        new Main("A").start();
        new Main("B").start();
        new Main("C").start();
        System.out.println("End");
    }

    public void run(){
        Singleton obj = Singleton.getInstance();
        System.out.println(getName() + ":obj = "+ obj);
    }

    public Main(String name){
        super(name);
    }
}

运行结果:

result.png
在以上代码中,如下条件判断时线程不安全的:
if (singleton == null){
    singleton = new Singleton();
}

在使用singleton == null判断第一个实例是否为null后,执行了下面的语句:

singleton = new Singleton();

但是在赋值之前,其他线程可能会执行singleton == null的判断。
因此定义getInstance方法为synchronized方法后才是严谨的Singleton模式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
        System.out.println("生成了一个实例");
        slowdown();
    }
    public static synchronized Singleton getInstance(){
        if (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }

    private void slowdown(){
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){

        }
    }
}

或者使用双重锁:

public class Singleton {
    private static volatile Singleton singleton = null;
    private Singleton(){
        System.out.println("生成了一个实例");
        slowdown();
    }
    public static  Singleton getInstance(){
        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    private void slowdown(){
        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){

        }
    }
}

在双重锁中我们需要为singleton加volatile修饰,因为singleton = new Singleton()可以拆解为3步:
1、分配内存
2、初始化对象
3、指向刚分配的地址
若发生了重排序,假设A线程执行了1,3,还没有执行2,B线程会直接返回还没初始化的instance了,volatile可以避免重排序。

参考《图解设计模式》【日】结城浩 著 杨文轩 译

相关文章

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 单例模式

    单例模式1 单例模式2

  • java的单例模式

    饿汉单例模式 懒汉单例模式

网友评论

      本文标题:单例模式

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