Singleton你真的懂单例模式吗?

单例模式是我们最常用的设计模式,但是你真的懂单例吗?

  • 饿汉式与懒汉式的区别?
  • 线程安全怎么解决?
  • 能防止反序列化吗?

首先说一下什么是单例模式
这个模式是很有意思,而且比较简单,但是我还是要说因为它使用的是如此的广泛,如此的有人缘,

  • 单例就是单一、独苗的意思,那什么是独一份呢?你的思维是独一份,除此之外还有什么不能山寨的呢?
  • 我们举个比较难复制的对象:皇帝
    中国的历史上很少出现两个皇帝并存的时期,是有,但不多,那我们就认为皇帝是个单例模式,在这个场景中,有皇帝,有大臣,大臣是天天要上朝参见皇帝的,今天参拜的皇帝应该和昨天、前天的一样(过渡期的不考虑,别找茬哦),大臣磕完头,抬头一看,嗨,还是昨天那个皇帝,单例模式,绝对的单例模式,
  • 先看类图:
    在这里插入图片描述

简单应用

先创建一个皇帝类

public class Emperor {
    private static Emperor emperor = null; //定义一个皇帝放在那里,然后给这个皇帝名字

    private Emperor() {
        //世俗和道德约束你,目的就是不让你产生第二个皇帝
    }
    public static Emperor getInstance(){
        if (emperor == null){
            emperor = new Emperor();
        }
        return emperor;
    }

    public static void emperorInfo(){
        System.out.println("我就是皇帝武则天....");
    }
}

再创建一个大臣类

public class Minister {
    public static void main(String[] args) {
        //第一天
        Emperor instance = Emperor.getInstance();
        Emperor emperor = instance;
        emperor.emperorInfo();//第一天见的皇帝叫什么名字呢?

        //第一天
        Emperor instance2 = Emperor.getInstance();
        instance2.emperorInfo();

        //第三天
        Emperor instance3 = Emperor.getInstance();
        instance3.emperorInfo();

    }
}

运行结果

我就是皇帝武则天....
我就是皇帝武则天....
我就是皇帝武则天....

这是单例模式的最简单应用

进阶

  • 饿汉式

缺点:不管是否使用,类装载完成时,一定会完成实例化

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO 单例模式
 * @date 2021-8-23 下午 10:28
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用
 * 缺点:不管是否使用,类装载完成时,一定会完成实例化
 */
public class Mgr01 {
    private static final Mgr01 INSTANCE = new Mgr01();
    private Mgr01(){}
    public static Mgr01 getInstance(){
        return INSTANCE;
    }

    public static void m(){
        System.out.println("M");
    }

    public static void main(String[] args) {
        Mgr01 mgr01 = Mgr01.getInstance();
        Mgr01 mgr02 = Mgr01.getInstance();
        System.out.println(mgr01 == mgr02);
    }
}

饿汉式的另一种写法

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO 01的另一种写法
 * @date 2021-8-23 下午 10:38
 * 
 */
public class Mgr02 {

    private static final Mgr02 INSTANCE ;
    static {
        INSTANCE = new Mgr02();
    }

    public static Mgr02 getInstance(){
        return INSTANCE;
    }

    public static void m(){
        System.out.println("M");
    }

    public static void main(String[] args) {
        Mgr02 mgr01 = Mgr02.getInstance();
        Mgr02 mgr02 = Mgr02.getInstance();
        System.out.println(mgr01 == mgr02);
    }
}
  • 懒汉式

优点:按需初始化
缺点:线程不安全

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2021-8-23 下午 10:40
 * lazy loading 懒汉式
 * 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
 */
public class Mgr03 {
    private static Mgr03 INSTANCE ;
    private Mgr03(){}
    public static Mgr03 getInstance(){
        if (INSTANCE == null){
            try{
                Thread.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }
    public static void m(){
        System.out.println("M");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr03.getInstance().hashCode());
            }).start();
        }
    }
}
  • 使用synchronized解决线程不安全问题

缺点:效率下降

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2021-8-23 下午 10:40
 * lazy loading 懒汉式
 * 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
 * 可以通过synchronized关键字来解决,但是效率会下降
 */
public class Mgr04 {
    private static Mgr04 INSTANCE ;
    private Mgr04(){}
    public static synchronized Mgr04 getInstance(){
        if (INSTANCE == null){
            try{
                Thread.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }
    public static void m(){
        System.out.println("M");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr04.getInstance().hashCode());
            }).start();
        }
    }
}
/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2021-8-23 下午 10:40
 * lazy loading 懒汉式
 * 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
 * 可以通过synchronized关键字来解决,但是效率会下降
 */
public class Mgr05 {
    private static Mgr05 INSTANCE;

    private Mgr05() {
    }

    public static Mgr05 getInstance() {
        if (INSTANCE == null) {
            //试图通过减小同步代码块的方式提交效率,然而不可行,判断
            synchronized (Mgr05.class) {
                try {
                    Thread.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr05();
            }
        }
        return INSTANCE;
    }

    public static void m() {
        System.out.println("M");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Mgr05.getInstance().hashCode());
            }).start();
        }
    }
}
  • 试图通过静态代码块的方式提高效率,但是需要双重检测
/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2021-8-23 下午 10:40
 * lazy loading 懒汉式
 * 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
 * 可以通过synchronized关键字来解决,但是效率会下降
 */
public class Mgr06 {
    private static volatile Mgr06 INSTANCE;

    private Mgr06() {
    }

    public static  Mgr06 getInstance() {
        if (INSTANCE == null) {
            //试图通过减小同步代码块的方式提交效率,然而不可行,判断
            synchronized (Mgr06.class) {
                //双重检测
                if(INSTANCE == null){
                    try {
                        Thread.sleep(1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr06();
                }
            }
        }
        return INSTANCE;
    }


    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Mgr06.getInstance().hashCode());
            }).start();
        }
    }
}
/**
 * @author Administrator
 * @version 1.0
 * @description: TODO 静态内部类方式
 * JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载
 * @date 2021-8-24 上午 8:40
 */
public class Mgr07 {
    private Mgr07(){

    }
    private static class Mgr07Holder{
        private final static Mgr07 INSTANCE = new Mgr07();
    }
    private static Mgr07 getInstance(){
        return Mgr07Holder.INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr07.getInstance().hashCode());
            }).start();
        }
    }
}
  • 枚举单例

不仅可以解决线程同步,还能防止反序列化,完美写法,线程安全由JVM负责

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2021-8-24 上午 8:47
 * 不仅可以解决线程同步,还能防止反序列化,完美写法,线程安全由JVM负责
 */
public enum Mgr08 {
    INSTANCE;

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr08.INSTANCE.hashCode());
            }).start();
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值