饿汉式:类加载时就会创建对象实例
懒汉式:类加载时不会创建对象实例,首次使用该对象时才会创建
1. 饿汉式
饿汉式单例模式:在【类加载】的时候就创建实例,也称为“急切初始化”。实现简单,线程安全。因为在类加载时就创建实例,不会出现多线程并发创建实例的问题。
缺点:
如果实例创建比较耗时或者占用资源较多,影响启动速度。
1.1 静态成员变量
- 私有构造方法
private Singleton(){}
- 在本类中创建本类对象
private static final Singleton instance = new Singleton();
- 对外提供公共访问方法,让外界获取该对象
public static Singleton getInstance(){return instance;}
代码示例:
public class Singleton{
private static Singleton instance = new Singleton(); // 在本类中创建本类对象
private Singleton(){}; // 构造函数私有化
// 获取对象实例的方法
public static Singleton getInstance() {
return instance;
}
}
1.2 静态代码块
- 私有构造方法
private Singleton(){}
- 声明Singleton类型的变量
private static Singleton instance
- 在静态代码块中进行赋值
static{instance = new Singleton();}
- 对外提供公共访问方法,让外界获取该对象
public static Singleton getInstance(){return instance;}
代码示例:
public class Singleton{
private static Singleton instance; // 声明Singleton类型的变量
// 在静态代码块中进行赋值
static{
instance = new Singleton();
}
private Singleton(){}; // 构造函数私有化
// 获取对象实例的方法
public static Singleton getInstance() {
return instance;
}
}
1.3 枚举
枚举实现单例模式也是极力推荐的,枚举类型时线程安全的,并且只会装载一次。而且枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。
如果不考虑浪费空间的情况下,选择枚举实现单例模式。
代码示例:
public enum Singleton5 {
INSTANCE;
}
2. 懒汉式
在第一次使用时创建实例
2.1 线程不安全
- 私有构造方法
private Singleton(){}
- 声明Singleton类型的变量
private static Singleton instance
- 对外提供公共访问方法,让外界获取该对象
代码示例:
public class Singleton1 {
private static Singleton1 instance;
private Singleton1(){};
public static Singleton1 getInstance(){
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
优点:延迟加载,节省资源。不影响启动性能。
缺点:线程不安全,多线程环境下可能会创建多个实例。
2.2 线程安全
方法上加入synchronized
关键字
代码示例:
public class Singleton2 {
private static Singleton2 instance;
private Singleton2(){};
public static synchronized Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
懒汉式单例模式(线程安全)通过在【getInstance】方法上加上【synchronized】关键字来保证线程安全。
优点:线程安全
缺点:每次调用【getInstance】方法都需要进行同步,会影响性能
2.3 双重检查
双重检查:在懒汉式线程安全的基础上进行优化,通过双重检查锁的方式来减少同步的开销。
这种方式在第一次检查实例是否为null时不需要进行同步,只有实例为null时才进行同步,从而提高了性能。
同时,在创建实例后再次检查实例是否为null,以防止在多线程环境下多个线程同时进入同步块创建多个实例。
双重检查锁存在的问题:在多线程的环境下,可能会出现空指针问题,由于jvm在实例化对象的时候会进行优化和指令重排序操作,要解决空指针问题,使用volatile
关键字。
public class Singleton {
// volatile保证可见性和有序性,防止jvm指令重排序
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;
}
}
2.4 静态内部类
静态内部类单例模式中实例由【内部类】创建,由于JVM在加载外部类的过程中,不会加载【静态内部类】的,只有内部类的【属性/方法】被调用时才会加载,并初始化其静态属性,静态内属性由于被
static
修饰,保证只被实例化一次,并且严格保证实例化顺序。
public class Singleton4 {
// 私有构造方法
private Singleton4() {
}
// 静态内部类
public static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
// 对外提供获取实例的方法
public static Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类单例模式是一种优秀的单例模式,没有加任何锁的情况下保证多线程下的安全,并且没有任何性能影响和空间的浪费。