什么是单例模式?
单例模式属于创建型模式.他提供了一种创建对象的最佳方式,这种模式只涉及到单一的类,该类的唯一职责就是创建自己的对象,同时确保这个对象只能被创建的一次.并且这个类提供一个访问该对象的唯一方式,直接访问,使用是不再需要实例化对象
注意:
- 单例类只能有一个实例.
- 单例类的实例必须是自己的创建且唯一的实例
- 单例类必须给其他所有对象提供这个一个实例
单例模式的两种实现
饿汉式
- 基本实现
public class Singleton {
private Singleton() {}
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance() {
return INSTANCE;
}
}
总结:这种方式简单易用,执行效率高,但是他在类加载的时候就初始化,如果该对象只是声明,在后续代码中并没有使用,那么他申请的内存无疑是浪费的
- 枚举实现
public enum Singleton {
INSTANE;
}
总结:这种方式利用了 enum 只会被加载一次的机制,实现了实例化对象.这也是最佳的实现单例的模式的方式,在不考虑内存是否浪费的情况下
懒汉式
- 基本实现(线程不安全)
public class Singleton {
private Singleton() {}
private static Singleton INSTANCE;
public static Singleton getInstance() {
if (INSTANCE == null){
new Singleton();
}
return INSTANCE;
}
}
总结:这种方式简单易用,执行效率高,且只有在该对象被调用时才会实例化,不会出现内存浪费.但是在多线程的情况下会出现问题,他是线程不安全的
- 基本实现(线程安全)
public class Singleton {
private Singleton() {}
private static Singleton INSTANCE;
public static synchronized Singleton getInstance() {
if (INSTANCE == null){
new Singleton();
}
return INSTANCE;
}
}
总结:这种方式在多线程的情况下也是安全的,但是因为加了 synchronized 关键字影响了执行效率
- 双重锁实现
public class Singleton {
private Singleton() {}
private static Singleton INSTANCE;
public static Singleton getInstance() {
if (INSTANCE == null){
synchronized (Singleton.class) {
if (INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
总结:这种方式使用双重锁机制,不是在每次请求的时候都要加锁,只有在进行创建对象时才会加锁,即保证了线程安全同时也保证了执行效率.但是这种也会有另一种情况发生 即 在多线程情况下,jvm 在进行实例化对象时会进行指令重排操作,进而可能出现空指针异常,这时需要加上 volatile 关键字进行修饰,保证变量的可见性和有序性
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;
}
}
- 静态内部类实现
public class Singleton {
private Singleton(){}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
总结:这种方式实现简单,并且一样可以保证线程安全且保证效率.因为他利用了 classloader 的加载原理 和 static 关键字的作用来保证静态内部类种的静态属性只会被创建一次,而且也只会在该对象被调用时才会创建实例对象