尚硅谷设计模式学习---[单例模式]

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尚硅谷设计模式学习---[单例模式]相关的知识,希望对你有一定的参考价值。

🚀🚀🚀尚硅谷传送门==>B站尚硅谷Java设计模式

❤❤❤感谢尚硅谷❤❤❤

🛴🛴🛴最近开始计划学习一下设计模式了,加油!!!



单例模式

一个类把自己的构造方法隐藏起来;
这个类仅存在一个对象实例,且该类对外界只提供一个可以获取对象的方法.

单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(重量级对象),经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等).

单例模式主要有五种:
饿汉式 ;懒汉式; 双重检查 ; 静态内部类 ;枚举


🛴1.饿汉式(静态常量版)

先把类的构造方法用私有修饰符private修饰,使其私有化;(外界不能通过new+构造方法的方式得到这个类的对象);
然后这个类在自己内部创建自己的对象;给外界提供一个公共方法去获取对象;

使用某类的static方法; 访问某类的static属性;构造某类的对象; 通过Class.forName()来加载类;都可以说是类加载了.

  • 使用饿汉式(静态常量版)在类加载的时候就完成实例化。避免了线程同步问题。
  • 需要注意的是类加载的方式有很多种,不能保证用的是getInstance()方法进行的类加载,那么此时的初始化singleton就没有懒加载的效果了;可能会浪费内存.

案例

public class StaticFinal {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958
        
        System.out.println(singleton1 == singleton2);// true

    }
}


// 饿汉式 ( 静态变量 ) ;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {
    }

    // 在自己内部创建对象;
    private  static Singleton singleton = new Singleton();

    //  给外界提供一个获取对象的 静态公有方法;
    public static Singleton getInstance() {
        return singleton;
    }
}

比如说Runtime类;就是用了饿汉式的单例模式;


🛴2.饿汉式(静态代码块版)

类实例化的过程放在静态代码块中

public class StaticCodeBlock {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}



// 饿汉式 ( 静态代码块 ) ;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {

    }

    // 在自己内部定义引用;
    private static Singleton singleton ;

    //  在静态代码块中创建对象;
    static {
        singleton = new Singleton();
    }

    //  给外界提供一个获取对象的 静态公有方法;
    public static Singleton getInstance() {
        return singleton;
    }
}

🛴3.懒汉式(线程不安全版)

懒汉式,在需要的时候才去创建实例,

达到了懒加载的效果,适合于单线程;

但是;由于这是单例模式;也就是说类仅有一份实例对象;要是放在多线程环境下;前面的线程创建了实例对象singleton之前; 正好有别的线程也执行了方法getInstance();它又去创建了一个实例对象.

实际开发不要用

public class ThreadNoSafe {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}


//  懒汉式 线程不安全版;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {
    }

    // 在自己内部定义引用;
    private static Singleton singleton;

    //给外界提供一个获取对象的方法;
    public static Singleton getInstance() {
        //当不存在实例对象时才创建;
        if(singleton == null){
            singleton=new Singleton();
        }
        return singleton;
    }

}

🛴4.懒汉式(线程安全之同步方法版)

在上面的基础上;让这个提供给外界的方法getInstance() 变为同步方法;
也就是说,在多线程环境中,每个线程过来执行这个方法时,他要排队,也就没有那种(一个线程判断if(singleton == null) 后,还没创建好对象实例,别的线程也跑进来) 的情况;
但是这样子虽然说线程安全了,可是效率也变低了啊;本来这个getInstance()方法就是为了得到实例对象的;把它变成同步方法;线程在这排着队;效率要低了.

public class ThreadSafeByMethod {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}


//  懒汉式 线程安全 ==>同步方法 版;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {
    }

    // 在自己内部定义引用;
    private static Singleton singleton;

    //给外界提供一个获取对象的方法;  此时getInstance() 为同步方法;
    public static synchronized Singleton getInstance() {
        //当不存在实例对象时才创建;
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}

🛴5.懒汉式(线程安全之同步代码块版)

当然,同步方法版不好用,也有人用同步代码块的方式这样子写;
但是这种方式是存在着线程不安全的问题;就像第三种的一样;多线程环境下;一个线程判断if(singleton == null) 后,还没创建好对象实例,别的线程也跑进来;最后产生多个实例对象.

实际开发不要用

public class ThreadSafeByCodeBlock {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}



//  懒汉式 线程安全 ==>同步代码块 版;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {
    }

    // 在自己内部定义引用;
    private static Singleton singleton;

    //给外界提供一个获取对象的方法;
    public static  Singleton getInstance() {
        //当不存在实例对象时才创建;
        if (singleton == null) {
            //用同步代码块;
            synchronized (Singleton.class){
                singleton = new Singleton();
            }
        }
        return singleton;
    }

}

🛴6.双重检查版

注意: ==> 用 volatile修饰的共享变量,每次的更新直接刷新到内存中去,对于其他线程都是可见的.

顾名思义,双重检查,判断检查两次;
这样的方式不但实现了懒加载效果,也是线程安全的,效率还高.

在多线程环境下,一个线程A正在创建对象实例时,比如说线程B也跑进来;这时候线程B发现还要判断if(singleton == null);进行判断后,由于线程A 过去的时候已经创建了实例对象;那么线程B自然就不用再去创建实例对象了.直接return返回吧.

还是比较推荐使用的方式

public class DuplicationCheck {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}


// 双重检查版;

class Singleton {

    //  将构造方法私有化;
    private Singleton() {
    }

    // 在自己内部定义引用;
    // 用 volatile修饰的共享变量,每次的更新对于其他线程都是可见的 ;
    private static volatile Singleton singleton;

    //给外界提供一个获取对象的方法;
    public static Singleton getInstance() {
        //当不存在实例对象时才创建;
        if (singleton == null) {
            //用同步代码块;
            synchronized (Singleton.class){
                //在这里再次进行判断检查;
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

🛴7.静态内部类版

首先回顾一下静态内部类;
当外部类加载时,静态内部类并不会被实例化;

当需要去调用getInstance()方法时,静态内部类才去加载;且仅加载一次.
(类的静态属性只会在第一次加载类的时候初始化)
(类初始化时,别的线程无法进入)

线程安全,有延迟加载效果,效率高

public class StaticInnerClass {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}


//静态内部类的方式

class Singleton {

    //  将构造方法私有化;
    private Singleton() {

    }

    //用静态内部类定义创建的私有静态常量对象;
    private static class SingletonObj {
        //在自己内部创建对象;
        private static final Singleton SINGLETON = new Singleton();
    }

    //  给外界提供一个获取对象的方法;
    public static Singleton getInstance() {
        //调用此方法时,才去加载静态内部类;
        return SingletonObj.SINGLETON;
    }
}

🛴8.枚举版

可解决线程同步问题,也防止反序列化对象

推荐使用

public class ByEnum {
    public static void main(String[] args) {
        //这里通过Singleton类提供的方法 ;获取Singleton类的对象;

        Singleton singleton1=Singleton.INSTANCE;
        Singleton singleton2=Singleton.INSTANCE;

        System.out.println(singleton1.hashCode());//460141958
        System.out.println(singleton2.hashCode());//460141958

        //注意看看两次获取的是 同一个对象;
        System.out.println(singleton1 == singleton2);// true
    }
}

//使用枚举的方式;

enum Singleton{
    INSTANCE;
}

以上是关于尚硅谷设计模式学习---[单例模式]的主要内容,如果未能解决你的问题,请参考以下文章

尚硅谷设计模式学习---[装饰者模式]

尚硅谷设计模式学习---[桥接模式(Bridge)]

尚硅谷设计模式学习(23)---[策略模式(strategy pattern)]

尚硅谷设计模式学习---[简单工厂模式,工厂方法模式,抽象工厂模式]

尚硅谷设计模式学习(17)---[迭代器模式(Iterator Pattern)]

尚硅谷设计模式学习(11)---[外观模式(Facade)]