单例模式。

单例模式:一个类只创建一个实例。

单例模式有两种实现方式。饿汉式,在类加载时立即初始化类实例。懒汉式,只有当使用时才创建类实例

@ThreadSafe
public class Singleton {

    // 饿汉模式
    private static Singleton singleton = new Singleton();
    
    // 防止其他类实例化此类
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return singleton;
    }
}

上面是“饿汉”模式的实现,它是线程安全的,但却不能延迟加载。也就是说这个单例在类加载时就已经初始化了,而不是在使用时才初始化,这在一定程度上造成了资源浪费。

单例还有一种“懒汉”模式,在使用到时才初始化。

@NotThreadSafe
public class Singleton {
    
    private static Singleton singleton;
    
    private Singleton() {
    }
    
    // 懒汉模式
    public static Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

上面这段代码实现了延迟加载,但却不是线程安全的。在多线程环境下,可能导致创建了多个新的对象。

@ThreadSafe
pubilc class Singleton {
    
    private static Singleton singleton;
    
    private Singleton() {
    }
    
    // 懒汉模式
    public static Singleton getInstance() {
        // 同步锁的加入
        synchronized(Singleton.class) {
            if(singleton == null) {
                singleton == new Singleton();
            }
        }
        return singleton;
    }
}

这个实现是线程安全的,但是每次调用getInstance方法时都需要获取同步锁,这是完全没必要的,严重降低了应用程序的性能。

完全可以在第一次创建时获取锁,后面不需要锁直接获取即可,基于此思想的实现一般称为“双重检测”(double check)。

@NotThreadSafe
public class Singleton {

    // volatile防止指令充排序
    private static Singelton singleton;
    
    private Singleton(){
    }
    
    public static Singleton getInstance() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

上面实现不是线程安全的,究其原因,singleton = new Singleton()这个动作,其实是分三步完成的。

1.为对象分配内存空间

2.初始化对象

3.设置singleton变量指向刚刚分配的内存空间

但在Java虚拟机中,由于指令重排序的原因,2,3步的操作可能对调。在多线程环境下,拿到的可能是一个没有初始化完成的singleton对象。

举个例子,线程A在第二个singleton == null 处,由于指令重排序的原因,执行了第一和第三步,此时singleton不为空,但是还是一个没有完全初始化的对象。此时线程B走到了第一个singleton == null 处,判断singleton非空,直接返回singleton对象,此时线程B拿到的是未完全初始化的对象,这样就导致了错误的发生。

下面的实现是线程安全的。

@ThreadSafe
public class Singleton {
    
    // 防止指令重排序
    private static volatile Singleton singleton;
    
    private Singleton(){
    }
    
    public static Singleton getInstance() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

当用懒汉式构造一个单例对象时,double check + volatile才是线程安全的。

静态内部类实现单例
public class Singleton {
    
    private Singleton(){
    }
    
    private static class SingletonHolder {
        private static final Singleton singleton = new Singleton();
    }
    
    public static Singelton  getSingleton() {
        return SingletonHolder.singleton;
    }
}

上面的 代码通过静态内部类,即保证了线程安全,又能做到延迟加载。

SingletonHolder是一个静态内部类,当外部类被加载的时候,它并不会创建SingletonHolder对象。只有当调用getSingleton()方法时,这个时候才会加载SingletonHolder,instance的唯一性,创建过程的安全性,都有JVM来保证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值