单例模式

单例模式,第1张

单例模式基本定义:程序运行时,在java虚拟机中只存在该类的一个实例对象。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

目录

饿汉式

懒汉式(线程不安全,多线程不推荐使用)

懒汉式(线程安全,同步方法,也不推荐用,效率底)

双重检测(推荐使用)

静态内部类方式(推荐使用)

枚举(推荐使用)


饿汉式
  1. 构造器私有化(不可被 new)
  2. 类的内部创建对象(类初始化时立刻实例化,可能会浪费内存)
  3. 对外部暴露一个静态的公共方法  getInstance

 

/**
 * 饿汉式单例模式
 */
public class ClassA {
    //1.私有化构造方法,外部不能new,限制产生多个对象
    private ClassA(){ }

    //2.在类的内部创建一个类的实例
    //类初始化时,立即实例化这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
    private static final ClassA instance = new ClassA();

    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    //方法没有同步,调用效率高!
    public static ClassA  getInstance(){
        return instance;
    }

    //测试
    public static void main(String[] args) {
        ClassA a = ClassA.getInstance();
        ClassA b = ClassA.getInstance();
        System.out.println(a==b);
    }
}

优点:

        这种方法在类加载的时候就进行实例化,避免线程同步问题;

缺点:

        在类加载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果,如果从开始都结束从未使用该实例,则会导致线程浪费

懒汉式(线程不安全,多线程不推荐使用)

用到的时候才实例化

 

/**
 * 懒汉式单例模式
 */
public class ClassB {
    //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
    private ClassB(){ }
    //2.在类的内部创建一个类的实例
    private static ClassB instance ;
    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    public static ClassB  getInstance(){
        if(instance == null) {
            instance = new ClassB();
        }
        return instance;
    }

    //测试
    public static void main(String[] args) {
        ClassB a = ClassB.getInstance();
        ClassB b = ClassB.getInstance();
        System.out.println(a==b);
    }
}

特点:

        起到Lazy Loading效果,但是只能在单线程下使用。

        如果在多线程下,一个线程在执行 if(instance == null) 的时候还未来得及往下执行,另一个线程也通过这调判断语句,这样就会产生多个实例,所以在多线程下不可使用。

结论:开发中  不要  使用

懒汉式(线程安全,同步方法,也不推荐用,效率底)

添加 synchronized

/**
 * 懒汉式单例模式
 */
public class ClassB {
    //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
    private ClassB(){ }
    //2.在类的内部创建一个类的实例
    private static ClassB instance ;
    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    public static synchronized ClassB  getInstance(){
        if(instance == null) {
            instance = new ClassB();
        }
        return instance;
    }

    //测试
    public static void main(String[] args) {
        ClassB a = ClassB.getInstance();
        ClassB b = ClassB.getInstance();
        System.out.println(a==b);
    }
}

特点:

        解决线程不安全问题

        效率低,每个线程在获取类的实例时,执行getInstance()方法都要进行同步,而其实可以只执行一次实例化代码,后面需要获得该类实例直接return就行,所以该方法效率低

 

结论:

        结论:开发中  不推荐  使用

双重检测(推荐使用)

/**
 * 使用双重校验锁来实现单例模式
 */
public class ClassE {
    //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
    private ClassE(){ }
    //2.在类的内部创建一个类的实例
    //volatile作用:解决命令重排问题
    private volatile static ClassE instance; 
    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    public static ClassE  getInstance(){
        if(instance == null){ //检查实例,如果为空,就进入同步代码块
            synchronized (ClassE.class){
                if(instance == null){ //再检查一次,仍未空才创建实例
                    instance = new ClassE();
                }
            }
        }
        return instance;
    }

    //测试
    public static void main(String[] args) {
        ClassE a = ClassE.getInstance();
        ClassE b = ClassE.getInstance();
        System.out.println(a==b);
    }
}

线程安全,加volatile的作用是禁止指令重排。

静态内部类方式(推荐使用)

有点类似饿汉式,但又能做到了延迟加载

/**
 * 使用静态内部类方式实现单例模式
 */
public class ClassC {
    //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象
    private ClassC(){ }
    //2.在类的内部创建一个类的实例
    private static class Holder{
        private static ClassC instance = new ClassC();
    }
    //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用
    public static ClassC  getInstance(){
        return Holder.instance;
    }

    //测试
    public static void main(String[] args) {
        ClassC a = ClassC.getInstance();
        ClassC b = ClassC.getInstance();
        System.out.println(a==b);
    }
}

        静态内部类(Holder)在外部类(ClassC)被装载的时候不会被实例化,当调用getInstance时,才会装载Holder,从而完成ClassC才会被实例化,且创建过程的线程安全性,由 JVM 来保证

 

线程安全,利用静态内部类实现延迟加载,效率高

枚举(推荐使用)

1

2

/**
 * 使用枚举方法实现单例模式
 */
public enum ClassD {
    //定义一个枚举的元素,它就代表了Singleton的一个实例。
    INSTANCE;
    //对外部提供调用方法:将创建的对象返回,只能通过类来调用
    public void otherMethod(){
        //功能处理
    }
}
    
    //测试
    public static void main(String[] args) {
        ClassD a = ClassD.INSTANCE;
        ClassD b = ClassD.INSTANCE;
        System.out.println(a==b);
    }

线程安全,实现简单,调用效率高,不能延时加载。

枚举本身就是单例模式,由JVM从根本上提供保障并且可以天然的防止反射和反序列化漏洞!需要继承的场景它就不适用了。枚举方式是Effective Java作者提倡的方式。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/877845.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存