乐趣区

关于java:干货分享经典设计模式之单例模式

设计模式千千万,总是单例最常见。

单例模式的定义

保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。

六种单例的创立形式

1. 饿汉式

public class Singleton {private static Singleton instance = new Singleton();
  private Singleton() {}
  public static Singleton getInstance() {return instance;}
}

长处:
基于类的加载机制,防止了多线程同步问题,加载速度快。

毛病:
在类加载的时候就实现初始化,没有懒加载,如果没有应用这个实例,会造成内存节约。

2. 懒汉式 - 线程不平安版

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

长处:
第一次调用是才初始化对象,避免浪费资源

毛病:
加载速度慢,线程不平安

3. 懒汉式 - 线程平安版(synchronized 加锁)

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

长处:
多线程中保障线程平安
毛病:
每次获取对象实例,都须要进行同步,造成不必要的同步开销。

4. 双重校验锁

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

长处:
线程平安,懒加载,缩小同步开销

毛病:
第一个获取对象速度稍慢,但其在某些状况下也会呈现生效的状况,并不是完满的形式。

这外面应用了两次判空:第一次为了不必要的加锁同步,第二次是确保在 instance 为 null 的状况下才创立实例,防止屡次创立。

办法中还是用了关键字 volatile 对变量进行润饰,有如下几个作用:

1. 在 Java 内存模型中 volatile 能够保障可见性,及避免程序指令重排序。

2. 对象的创立分为如下几个步骤:

instance = new Singleton();
  • 1. 为 instance 分配内存空间
  • 2. 初始化 instance
  • 3. 将 instance 指向内存地址

如果不加 volatile 的话,程序的执行程序就可能变成 1 ->3->2, 多线程中就会导致线程获取一个没有初始化的实例。例如线程 a 执行了 1,3, 此时线程 b 调用 getInstance()发现 instance 不为空,返回 instance, 但此时 instance 还未初始化。

5. 动态外部类

public class Singleton {private Singleton() {}
  public static Singleton getInstance() {return SingletonHolder.sInstance;} 
  private static class SingletonHolder {private static final Singleton sInstance = new Singleton();
  }
}

第一次加载类的时候不会初始化 instance, 只有第一次调用 getInstance()的时候才会进行加载 SingleHolder 并初始化 instance, 保障线程平安, 也能保障实例惟一,举荐应用这种形式。

6. 枚举

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

默认枚举单例的创立是线程平安的,并且任何状况下都是单例。

以上就是 6 中常见的单例创立模式,按需应用吧。

单例的应用场景

  • 整个我的项目须要一个共享拜访点或者数据
  • 创立一个对象须要消耗的资源太多,比方拜访数据库资源等
  • 工具类对象
退出移动版