浅谈singleton单例模式


声明:本文转载自https://my.oschina.net/u/4006148/blog/3107356,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

一、前言

单例模式比较简单,可以说没有复杂的调用和接口的设计,就是一个简单的类,只是要求这个类只生成一个对象,无论什么时候都要保证这一点,因此只能生成一个实例的模式就是单例模式。

二、类的加载

类的加载是通过类加载器(Classloader)完成的,它既可以是饿汉式加载类,也可以是懒汉式加载,这跟不同的JVM实现有关。加载完类后,类的初始化就会发生,如果是对一个类的主动使用就会初始化对象,对类的被动使用不会对类进行初始化,比如final修饰的静态变量如果能在编译时就确定变量的取值,会被当做常量,作为对一个类的被动使用不会导致类的初始化。以下情况类被初始化:

类初始化的一些规则:

  1. 类从顶到底的顺序初始化,所以声明在顶部的字段遭遇底部的字段初始化;
  2. 超类早于子类和衍生类的初始化;
  3. 如果类的初始化是由于访问静态域而触发,那么只能声明静态域的类才被初始化,而不会触发超类的初始化或者子类的初始化,即使静态域被子类或子接口或者它的实现类锁引用;
  4. 接口初始化不会导致父接口的初始化;
  5. 静态域的初始化时在类的静态初始化期间,非静态域的初始化是在类的实例创建期间,这意味着静态域初始化在非静态域之前;
  6. 非静态域通过构造器初始化,子类在做任何初始化之前构造器会先调用父类的构造器,它保证了父类非静态或实例变量初始化早于子类;

三、单例模式的特点

单例模式有以下特点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

目的:单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个打印服务,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

四、饿汉式单例

public class Singleton {
  private Singleton() {}
  private static final Singleton single = new Singleton();
  //静态工厂方法 
  public static Singleton getInstance() {
      return single;
  }
}

因为这本身就是static修饰的方法,所以是在类加载的时候被创建,后期不会再改变,所以线程是安全的。

五、懒汉式单例

public class SingletonTest {
    public static SingletonTest singleton = null;
    public static SingletonTest getInstance(){
        if(singleton == null){
            singleton = new SingletonTest();
            System.out.println("创建一次");
        }
        return singleton;
    }
    public void show(){
        System.out.println("我是江疏影");
    }

    public static void main(String[] args) {
        SingletonTest singleton = SingletonTest.getInstance();
        SingletonTest singleton1 = SingletonTest.getInstance();
        singleton.show();
        singleton1.show();
        if(singleton==singleton1){
            System.out.println("该对象的字符串表示形式:");
            System.out.println("singleton :"+singleton.toString());
            System.out.println("singleton1:"+singleton1.toString());
        }
    }
}

懒汉式方法总是会出现这样或那样的问题的,因为考虑到了多线程机制,实现起来比较麻烦,并且还会出现问题,就算是使用了一定的解救办法(同步、加锁、双重判断)的办法,性能还是被损耗了,因此懒汉式方法的弊端非常大。

六、双检锁/双重校验锁

描述:采用双锁机制,安全且在多线程情况下能保持高性能。多线程安全

package designMode;

public class Singleton {
    private volatile static Singleton singleton;
    public static synchronized Singleton getSingleton(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

或者使用如下方式,双重判断,第二次判断就是防止已经有一个对象产生了,因此也可以达到相应的目的。

package designMode;

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

这里的两次判断,第一判断:效率,第二判断:避免同步。之所以这样是因为避免加锁后,再次加锁。大大增强了执行效率。

七、总结

在这个单例模式中,我希望大家不要只知道单例的思想,更要知道类的加载和初始化时机,以及多线程的机制,我想这才是真正有意义的呢。

本文发表于2019年09月18日 15:00
(c)注:本文转载自https://my.oschina.net/u/4006148/blog/3107356,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1407 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1