23种设计模式之单例模式

单例模式

参考:

https://how2j.cn/k/class-object/class-object-singleton/349.html#nowhere

https://www.bilibili.com/video/av68172551

https://blog.csdn.net/baolingye/article/details/101106783

核心作用

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

优点

  1. 单例模式只生成一个实例,减少系统的性能开销
  2. 单例模式可以在系统设置全局访问点,优化共享资源访问

常见的五种单例模式实现

一、饿汉式

特点:线程安全,调用效率,不能延时加载

缺点:无论这个类有没有被调用到,都会首先加载里面的资源,占用一定的内存空间,如果这个类长时间没有被用到,那么容易会造成空间浪费的结果。

/*
	精髓所在
*/
public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){
    }
 
    //准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个
    private static GiantDragon instance = new GiantDragon();
     
    //public static 方法,提供给调用者获取第7行定义的对象
    public static GiantDragon getInstance(){
        return instance;
    }
}
/*
	测试,后面相同
*/
public class TestGiantDragon {
    public static void main(String[] args) {
        //通过new实例化会报错
//      GiantDragon g = new GiantDragon();
         
        //只能通过getInstance得到对象      
        GiantDragon g1 = GiantDragon.getInstance();
        GiantDragon g2 = GiantDragon.getInstance();
        GiantDragon g3 = GiantDragon.getInstance();
         
        //都是同一个对象
        System.out.println(g1==g2);		// true
        System.out.println(g1==g3);		// true
    }
}

二、懒汉式

懒汉式单例模式与饿汉式单例模式不同,只有在调用这个类的时候,才会创建实例

特点:线程安全,调用效率不高,可以延时加载

缺点:效率较

public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){       
    }

    //准备一个类属性,用于指向一个实例化对象,但是暂时不创建内存空间,令其指向null
    private static GiantDragon instance;
      
    //public static 方法,返回实例对象		// 若存在并发,可添加synchronized修饰符
    public static GiantDragon getInstance(){
        //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
        if(null==instance){
            instance = new GiantDragon();
        }
        //返回 instance指向的对象
        return instance;
    }
      
}

三、DCL懒汉式(双重检测锁模式)

特点:不需要对整个方法进行同步,缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率

缺点:因为不是在类加载时就创建对象,因此存在线程安全问题,当第一个线程执行到创建对象的方法时,但还未出方法返回,此时第二个线程进入,发现instance不为空,但第一个线程此时还未出去,可能发送意想不到的安全问题

public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){       
    }

    //准备一个类属性,用于指向一个实例化对象,但是暂时不创建内存空间,令其指向null
    //使用避免指令重排带来的线程安全问题
    private static volatile GiantDragon instance;
      
    //public static 方法,返回实例对象
    //使用同步代码块提高效率
    //现在不需要对整个方法进行同步,缩小了锁的范围,只有第一次会进入创建对象的方法,提高了效率
    public static GiantDragon  getInstance(){
        //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
        if(null==instance){
            synchronized (GiantDragon.class){
                if(null==instance){
                	instance = new GiantDragon();
                }
            }
        }
        //返回 instance指向的对象
        return instance;
    }
      
}

四、静态内部类实现

使用静态内部类解决了线程安全问题,并实现了延时加载

缺点:反射机制有可能会破坏单例化

public class GiantDragon {
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){       
    }

    //不会在外部类初始化时就直接加载,只有当调用了getInstance方法时才会静态加载,线程安全,final保证了在内存中只有一份
    private static class InnerClass{
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }

    public static SingletonDemo4 getInstance() {
        return InnerClass.instance;
    }
  
}

五、枚举单例

最推荐使用的单例模式

特点:线程安全,调用效率高

缺点:不能延时加载(但是安全

//枚举方式实现单例模式
public enum SingletonDemo5 {
    INSTANCE;

    public static SingletonDemo5 getInstance() {
        return INSTANCE;
    }
}

class SingletonDemo5Test {
    public static void main(String[] args) {
        SingletonDemo5 instance = SingletonDemo5.getInstance();
        SingletonDemo5 instance1 = SingletonDemo5.getInstance();
        System.out.println(instance == instance1); //true
    }
}
posted @ 2020-03-11 01:06  lorz5  阅读(159)  评论(0编辑  收藏  举报