美文网首页IT阔论
创建型模式-单例模式

创建型模式-单例模式

作者: 七佰 | 来源:发表于2018-03-04 13:40 被阅读11次

单例模式是一种自己生成对象的全局实例。当访问这个类的对象的时候,不需要在创建。

单例模式一共有6种衍生。

  1. 非线程安全的懒汉单例模式,它是最基础的单例模式实现,由于非线程安全,不推荐使用。
public class Singleton_1 {
    private int i = 0;

    private static Singleton_1 instance;

    private Singleton_1(){}

    public static Singleton_1 getInstance(){
        if(instance == null){
            instance = new Singleton_1();
        }
        return instance;
    }

    public void setValue(int i){
        this.i = i;
    }
}

2.线程安全的懒汉单例模式
与非线程安全的懒汉单例模式相比,加了synchronized锁,因此是线程安全的。可以在多线程下工作.由于使用了synchronized锁,之前,由于synchronized锁的效率的低下,不推荐使用,但是JDK1.6以后,synchronized锁与
LOCK一样使用了CAS(?),效率得到优化,真正应用场景的使用待考证.

public class Singeton_2{
    private static Singeton_2 instance;

    private Singeton_2(){};

    public static synchronized Singeton_2 getInstance(){
        if(instance==null){
            instance = new Singeton_2();
        }
        return instance;
    }
}
  1. 饿汉式单例模式
    由于饿汉单例模式在类加载时就初始化,避免了多线程的出现.但是由于不是懒加载机制,造成内存浪费.
public class Singleton_3{
    private static Singleton_3 instance = new Singleton_3();
    private Singleton_3(){};

    public static Singleton_3 getInstance() {
        return instance;
    }
}
  1. 双重锁单例模式
    哪双重锁?使用volatile关键字,保证了实域的可见性.使用过了synchronized,保证了操作的原子性.
public class Singleton_4 {
    private static volatile Singleton_4 instance;

    private Singleton_4(){};

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

5.静态单例模式
由于饿汉单例不能实现延迟加载的功能,我们利用内部类不会与外部类绑定初始化的机制,只有被调用时才会初始化对象,并且由JVM加载时负责线程安全.

public class Singleton_5 {
    private static class SingletonHolder {
        private static final Singleton_5 INSTANCE = new Singleton_5();
    }

    private Singleton_5(){};

    public static Singleton_5 getInstance(){
        return SingletonHolder.INSTANCE;
    }
}
  1. 枚举单例模式
    这种写法太他妈的优雅了.自JDK1.5开始,这种方式被网上推举成最佳写法.这里引申一个概念,

在JDK5 中提供了大量的语法糖,枚举就是其中一种。
所谓 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是但是更方便程序员使用。只是在编译器上做了手脚,却没有提供对应的指令集来处理它。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  

这段代码被反实例化后,

public final class Singleton extends Enum<Singleton> {
      public static final Singleton INSTANCE;
      public static Singleton[] values();
      public static Singleton valueOf(String s);
      static {};
}

是不是很像饿汉单例,虚拟机会保证一个类的<clinit>() 方法在多线程环境中被正确的加锁、同步。所以,枚举实现是在实例化时是线程安全。
接下来看看序列化问题:

Java规范中规定,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。
在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的 valueOf() 方法来根据名字查找枚举对象。
也就是说,以下面枚举为例,序列化的时候只将 DATASOURCE 这个名称输出,反序列化的时候再通过这个名称,查找对于的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。

最佳实践:
参见Spring源码的版本4.3.4,Spring依赖注入Bean实例的实现方式就是通过单例.
源码如下:

/**
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     * @param beanName the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
     */
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

拓展内容:
在写这篇的时候,发现对于多线程的锁的概念有点模糊不清.这里稍加说明,以后会专门写一篇多线程的文章.


image.png

上图中JAVA的内存模型 会产生多线程编程中的数据“脏读”等问题.
它具有三重特性.

  1. 可见性,指的是线程内部的状态对于外部是可见的.用volatile,synchronized,final修饰的变量,就具有可见性.

  2. 原子性,这个大家应该比较熟悉,在数据库操作中也会有.就是任何操作都是最小细度操作,额,也就是具有原子性.使用Synchronized和Lock\Condition都可以使操作具有原子性.

  3. 有序性,volatile和synchronize都可以使操作具有有序性.volatile禁止指令重新排列,synchronized是通过同一变量同一时刻只允许一条线程对其进行lock操作.

源码地址:https://github.com/walker0828/DesignPatterns.git
参考文档:
http://www.runoob.com/design-pattern/singleton-pattern.html
https://www.cnblogs.com/chengxuyuanzhilu/p/6404991.html
https://www.cnblogs.com/zhengbin/p/5654805.html
http://www.importnew.com/24082.html

相关文章

  • 单例模式

    单例 单例模式,是一种设计模式,属于创建型设计模式,还有一种创建型设计模式,工厂模式。设计模式总共有23种,三大类...

  • 开发之设计模式-单例模式

    设计模式 设计模式分为三大类:创建型、结构型、行为型在Java中有24中设计模式 创建型:单例 1、为什么用单例模...

  • 【设计模式】创建型设计模式汇总

    创建型设计模式汇总 1. 单例模式 1.1 单例模式的定义 一个类只允许创建一个对象或实例。 1.2 单例模式的作...

  • 23种设计模式学习总结

    创建型设计模式 主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码合使用代码。 单例模式 单例模式用来...

  • 2.架构设计(单例模式设计)

    1.设计模式分为三个类 创建型 结构型 行为型 2.创建型:单例模式 为什么用单例模式?如果你看到这个问题,你怎么...

  • Python 之单例模式

    简介:单例模式(Singleton Pattern) 是最简单的设计模式之一,属于创建型的设计模式。单例模式涉及到...

  • PHP常用设计模式

    # 创建型 单例模式 工厂模式 工厂抽象模式 原型模式 建造者模式 # 结构型 # 行为型 # 3.注册模式 # ...

  • 设计模式分类

    经典23种设计模式: 创建型设计模式: Singleton Pattern(单例模式) PrototypePatt...

  • PHP设计模式—创建型设计模式

    ** 创建型设计模式 **: 单例模式(Singleton Pattern) 工厂方法模式(Factor Patt...

  • S4. 单例模式

    单例模式(Singleton) 介绍 单例模式是创建型设计模式,即用于创建对象的设计。其能够保证当前系统仅存在一个...

网友评论

    本文标题:创建型模式-单例模式

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