设计模式 - 单例设计模式

   设计模式

  •     - 创建型模式

  •         - 单例设计模式

  •             - 饿汉式 :::类加载就会导致该单例对象被创建::

  •                 - 静态变量方式

  •                 - 静态代码块方式

  •             - 懒汉式:::首次使用该单例对象才会被加载::

思维导图

设计模式 - 单例设计模式_第1张图片

单例模式的定义与特点

在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点

单例模式的优点和缺点

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。


单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

单例模式看起来非常简单,实现起来也非常简单。单例模式在面试中是一个高频面试题。希望大家能够认真学习,掌握单例模式,提升核心竞争力,给面试加分,顺利拿到 Offer。

小案例加深理解

单例设计模式分为:饿汉式 、 懒汉式 

饿汉式的实现方式又分为 :静态代码块方式 和 静态变量方式

饿汉式 : 饿汉式类加载就会导致单例创建,如果实例用不到就会导致资源浪费。

静态变量方式

/**
 * 单例模式
 * 懒汉式
 * 静态变量方式
 */
public class Singleton1 {
    private Singleton1(){}

    private static Singleton1 instance = new Singleton1();

    public static Singleton1 getInstance(){
        return instance;
    }
}

静态代码块方式

/**
 * 单例模式
 * 懒汉式
 * 静态代码块
 */
public class Singleton2 {
    private Singleton2(){}

    private static Singleton2 instance;


   static {
       instance= new Singleton2();
   }

    public static Singleton2 getInstance(){
        return instance;
    }

}

单例模式下一个类的实例只允许存在一个

下面可以简单验证一下

public static void main(String[] args) {

    System.out.println("饿汉式 静态变量方式:"+(Singleton1.getInstance() == Singleton1.getInstance()));

    System.out.println("饿汉式 静态代码块方式:"+(Singleton2.getInstance() == Singleton2.getInstance()));


}

都为true 说明获取的都是一个实例对象 

懒汉式 : 第一次被访问才会去创建这个实例

/**
 * 单例模式
 * 懒汉式
 */
public class Singleton {
    private Singleton(){}

    private static Singleton instance;

    /*这里必须要加一个同步锁 如果是多线程
      线程1进入if 然后线程2拿到了cpu执行权 线程1只能等 这样线程1和线程2都进入了if 会导致创建两个实例
    * */
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

以上代码还是有问题的,除了第一次赋值,之后都是读操作,而读操作是线程安全的不需要加锁,但是偏偏还加了锁,就会导致资源浪费。

所以可以使用双重检查模式,并且加上 volatile 关键字

package singleton.demo2;

/**
 * 单例模式
 * 懒汉式
 */
public class Singleton {
    private Singleton(){}

    private static volatile Singleton instance;

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

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 synchronized,否则将存在线程非安全的问题。如果不删除这关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

双重检查的方式显得比较繁琐 ,还能使用静态内部类实现懒加载。

JVM在加载外部类的时候不会加载静态内部类的,只有当静态内部类的属性或者方法被访问的时候才会被加载。

package singleton.demo2;

public class Singleton2 {

    private Singleton2(){}

    private static class singleton{
        private static final Singleton2 instance = new Singleton2();
    }

    public static Singleton2 getInstance() {
        return singleton.instance;
    }
    

说明:
第一次加载singlcton类时不会去初始化INSTAMCE,只有第一次调用getInstanse,虚拟机加载SinglstonHlalda工并初始化INSTANCE,这样不仅能确保线程安全,也能保证singleton类的唯一性。
小结:
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
 

你可能感兴趣的