单例设计模式

单例模式有5种实现方式:饿汉、懒汉、双重校验锁、静态内部类和枚举

/**
 *
 * --------------|------------|-------------|--------------|
 *  单例模式实现  |是否线程安全  |是否为懒加载  |是否防止反射构建 |
 *  ------------|-------------|------------|--------------|
 *  双重锁检验   |     是      |     是     |      否      |
 *  -----------|-------------|------------|-------------|
 *  静态内部类  |     是      |     是     |      否     |
 *  ----------|-------------|------------|------------|
 *     枚举   |     是      |     否     |      是     |
 *
 */

// 一、饿汉
//    优点:类加载的时候创建一次实例,避免了多线程同步问题
//    缺点:即使单例没被用到也会创建,浪费内存
public class Singleton {

    //饿汉普通
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }

    //饿汉变种
    private static Singleton instance = null;
    static {
        instance = new Singleton();
    }
    private Singleton(){};
    public static Singleton getInstance(){
        return instance;
    }
}


//二、懒汉--(线程非安全)
//    优点:需要时才去创建
//    缺点:没有考虑线程安全问题,多个线程并发调用getInstance,可能会创建多个实例
class Singleton2{

    private static Singleton2 instance = null;
    private Singleton2(){}
    public static Singleton2 getInstance(){
        if(instance == null){
            return new Singleton2();
        }
        return instance;
    }
}
//懒汉--(线程安全)
//  缺点:性能问题,添加了synchronized的函数比一般方法慢得多,若多次调用getInstance,则累积的性能损耗特别大。
class Singleton3{

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


//三、双重校验
/**
 * 隐患:(此处涉及到JVM的指令重排)
 *      Java中的 instance = new Singleton(),会被编译器编译成以下JVM指令
 *          memory = allocate();  //1、分配对象内存空间
 *          ctorInstance(memory);  //2、初始化对象
 *          instance = memory;  //3、设置instance指向刚才分配的内存地址
 *      但是这些指令并不是一成不变的,可能会经过JVM和CPU的优化而改变
 *          memory = allocate();    //1、分配对象内存空间
 *          instance = memory;  //3、设置instance指向刚才分配的内存地址
 *          ctorInstance(memory);   //2、初始化对象
 *      当线程A执行完 1、3指令后,instance对象还未初始化,但已经不再指向null。此时如果线程B抢占CPU资源,
 *      执行if(instance == null) 的结果会返回false,从而返回一个没有初始化完成的对象
 *
 * 解决方案:volatile(只有成员变量才能使用它)
 *      作用:
 *          ①:多线程主要围绕可见性和原子性展开,使用volatile修饰变量,保证了其在多线程之间的可见性,即
 *   每次读取到的volatile变量,一定是最新的数据
 *          ②:使用volatile会禁止JVM指令重排序,在一定程度上降低了代码执行效率。
 *
 *
 */
class Singleton4{

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

//使用volatile修饰成员变量
class Singleton5{
    private static volatile Singleton5 instance = null;
    private Singleton5() {}
    public static Singleton5 getInstance(){
        if(instance == null){
            synchronized (Singleton5.class){
                if(instance == null){
                    return new Singleton5();
                }
            }
        }
        return instance;
    }
}

//第四种:静态内部类
class Singleton6{

    private static class LazyHolder{
        private static final Singleton6 instance = new Singleton6();
    }
    private Singleton6(){}
    public static Singleton6 getInstance(){
        return LazyHolder.instance;
    }
}

//第五种:枚举
//优点:既能避免多线程同步问题,又能防止反序列化重新创建新的对象。
enum Singleton7{
    INSTANCE;
    public void xx(){}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值