单例模式
工具类、内存类、网络类、大模型预加载等场景中,公用性的这类对象仅需要一份实例在堆内存中,因此诞生出单例模式
线程安全的饿汉式实现
静态常量在类加载时创建,从始至终保有单例
public class Singleton {
private static final Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
private Singleton() {}
}
线程不安全的懒汉式实现
懒汉式的想法是未使用不创建,只有第一次使用时才创建,之后一直保有单例。但是此方法是线程不安全的,当线程A刚刚执行完(1)时,线程B此时执行到(1)还处于未创建状态,因此也会去执行(2),最终将会创建两份实例
public class Singleton{
private static Singleton instance = null;
public static Singleton getInstance(){
if(instance == null) { // (1)
instance = new Singleton(); // (2)
}
return instance;
}
private Singleton() {}
}
线程安全的懒汉式
方法加锁保证线程安全和单例,锁粒度较大造成性能下降
线程安全的懒汉式可以形象化为一个场景:所有人在门外排队办业务,每次只允许进入一个人,进去后大门上锁。当日办理名额已经满员时,排队的所有人仍然要依次进大门去尝试办理
public class Singleton{
private static Singleton instance = null;
public static synchronized Singleton getInstance(){
if(instance == null) {
instance = new Singleton();
}
return instance;
}
private Singleton() {}
}
双重校验锁懒汉式
减小锁粒度,优先保证读取性能,只有写时才加锁。使用volatile不仅是为了写操作对其他线程的可见性,同时是为了禁止指令重排序:new Singleton()的实现逻辑并不是原子的:分配内存、初始化、引用赋值,这三个操作可能被重排序先引用赋值,使得if(instance == null)校验不通过而返回空instance
双重校验锁可以形象化为一个场景:所有人在进门后排队等待到业务窗口办业务,窗口每次只服务一个人。当日办理名额已经满员时,只有排队的人会尝试再去办,而新来的人在门口就知道名额已满直接离去
public class Singleton{
private static volatile Singleton instance;
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
private Singleton() {}
}