最浪漫的模式——单例

引言

你是我的唯一,我是你的单例。

大情至简、大爱至专
设计模式很多,最 浪漫 的莫过于单例。
因为单例的重要性和易用性,被大家广泛的应用于编码当中。
正因为其易用,反而被大家忽略了其重要性,产生了一些不必要的麻烦。

1、为什么需要单例

有的对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志、驱动程序等。事实上,这类对象也只能有一个实例,如果制造了多个实例,就会导致许多问题产生。

2、初始化全局变量和单例模式的区别

如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象,万一这个对象非常耗费资源,而程序在本次执行中却没有用 到,就形成了浪费。而单例模式可以在需要的时候才创建对象,这就是延迟实例化

一、第一种单例模式:懒汉式

java在实例化对象的时候需要用到new关键字,前提是实例化的类要有公开的构造器,那么就可以通过new来创建多个对象。
如果被实例化的类没有公开的构造器,而是一个私有的构造器,那么如何来获取类对象呢?
这也是单例的巧妙之处,private的构造器是无法在本类之外调用的,所以只能在本类中实例化。
那么外部想要得到这个实例,就需要提供一个静态的方法将实例化好的对象返回出去。
懒汉式单例的要素:静态的本类全局变量、私有的构造器、getInstance方法

      public class MyClass {
        //静态的本类成员变量
        private static MyClass myClass;
        //私有的构造器
        private MyClass() {
        }
        // 向外界提供的静态方法
        public static MyClass getInstance() {
           if (myClass == null) {
              myClass = new MyClass();
           }
           return myClass;
        }
     }

这就是常见的懒汉式单例,因为使用的时候才开始创建(延迟实例化),所以称为“懒”汉式。
懒汉式虽然易用,但是其在线程安全方面仍有一些问题:
假设有两个线程Thread1和Thread2同时调用getInstance方法。
线程2在线程1已经创建出MyClass对象并且已经刷新内存之后调用不会出现问题。
但是线程1刚创建出MyClass对象时还没有刷新内存之前,线程2还是认为myClass对象为null。
那么就会创建出第二个myClass对象,所以线程是不安全的。
这种问题可以通过线程锁来解决:

        //向外界提供的静态方法
        public synchronized static MyClass getInstance() {
           if (myClass == null) {
              myClass = new MyClass();
           }
           return myClass;
        }
使用线程锁可以规避多线程情况下产生不同的myClass对象,但是这种方法仍有弊端。
因为synchronized关键字会造成线程等待而降低程序的性能,同步一个方法可能造成程序运行的效率下降十倍百倍。
但是在不需要频繁操作getInstance方法时这种处理方式还是非常简洁实用的。
那么如何在线程安全的情况下又不降低程序的性能呢?这就是第二种单例模式。

二、第二种单例模式:饿汉式

之所以称为饿汉式,是因为其在本类初始化时就急切的创建类对象,在调用getInstance方法时直接返回创建好的对象。

public class MyClass {
    // 静态的本类成员变量
    private static MyClass myClass = new MyClass();
    //私有的构造器
    private MyClass() {
    }
    //向外界提供的静态方法
    public static MyClass getInstance() {
        return myClass;
    }
}

这种方式在本类创建时就实例化本类对象,所以避开了延迟实例化,是一种线程安全的做法。
其缺点是依然静态初始化本类对象。
如何在使用延迟实例化的方式下使用线程安全的单例模式, 这就提到了第三种单例模式。

三、第三种单例模式:双重加锁

利用双重加锁,首先检查是否实例已经创建,如果尚未创建,才进行同步
这样一来,只有第一次会同步,这正是我们想要的。

public class MyClass {
    // 静态的本类成员变量
    private volatile static MyClass myClass;
    // 私有的构造器
    private MyClass() {
    }
    // 像外界提供的静态方法
    public static MyClass getInstance() {
        if (myClass == null) {
            synchronized (MyClass.class) {
                if (myClass == null) {
                    myClass = new MyClass();
                }
            }
        }
        return myClass;
    }
}
volatile 关键字保证线程的并发下myClass能够强制刷新内存,通知其余线程对象发生的改变。
synchronized 在第一次初始化时进行同步,在同步过程中再进行一次判断,从而保证MyClass对象只有一个实例。

四、总结

单例模式有四种常见的形式:
1:懒汉式:简单、经典、线程不安全
2:同步懒汉式:简单、经典、线程安全、性能降低
3:饿汉式:简单、经典、线程安全、但是没有使用延迟实例化
4:双重加锁:相对复杂、线程安全、使用延迟实例化


长路漫漫,菜不是原罪,堕落才是原罪。
我的CSDN:https://blog.csdn.net/wuyangyang_2000
我的简书:https://www.jianshu.com/u/20c2f2c3560a
我的掘金:https://juejin.im/user/58009b94a0bb9f00586bb8a0
我的GitHub:https://github.com/wuyang2000
个人网站:www.xiyangkeji.cn
个人app(茜茜)蒲公英连接:https://www.pgyer.com/KMdT
我的微信公众号:茜洋 (定期推送优质技术文章,欢迎关注)
Android技术交流群:691174792

以上文章均可转载,转载请注明原创。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值