[设计模式] - No.2 Singleton 单例模式

最近面试中汽研,考官问到了单例模式。然后没有怎么搞清楚,结果在笔试的时候又考到了,特此学习一下,在这里记录。

单例模式,简单地说,就是我们在某些场景下可能需要一些单例类。什么叫单例类呢?我们知道,普通的类是通过开放他的构造函数,然后让外部程序去生成、调用它的对象。通常情况下,我们的类应该只关心内部实现的逻辑是什么样子的,而不应该关心外部是怎么调用它的。但是单例类,是将其构造函数设置为私有的,外部不能任意构造该函数。该类对应的对象只能有一个,并且应该有该类返回对象。

那么什么样的场景会需要单例类呢?比如,一个党只能有一个书记的对象。再比如说,一些临界资源设备对应的调用该设备的类应该被设置为单例类,因为这些设备资源是不能被同时使用的。

所以,现在简单的来看:单例模式是这样一种模式,我们的类将其构造函数私有化,然后只能通过某些方法,使该类返回一个对象。单例类内部至少应该包含以下几个函数:

1. -SingleObject() //私有化的构造函数

2. +getInstance():SingleObject  //返回对象的方法

3. +function() //该对象功能 

使用场景如下:

 public static void main(String args[]){
        Singleton object = Singleton.getInstance();
        object.function();
    }

单例模式的实现方式有很多,下面只介绍四种:懒汉式--线程不安全(最简单)、懒汉式--线程安全(加锁)、饿汉式(在类加载时就初始化)、DCL双重校验

1. 懒汉式-线程不安全:

public class Singleton {
    public static Singleton singleton;

    private Singleton(){

    }

    public void function(){
        System.out.println("Hello World");
    }

    public static Singleton  getInstance(){
        if (singleton == null){  // 1
            singleton = new Singleton(); // 2
        }
        return singleton;
    }
}

为什么说这种是多线程不安全的呢?学过操作系统的应该都知道,这段代码段并没有加锁,假设以下场景:

假设线程A进行到1处,并且检测到singleton引用为null。假如此时,线程B进程检测到singleton为null,并且顺利的创建了singleton。线程A继续工作,直接创建新的singleton,此时程序出错。

所以,这就解释了为什么上面的程序不是线程安全的。那么如何改进呢?加锁!

2. 懒汉式-线程安全:

public class Singleton {
    public static Singleton singleton;

    private Singleton(){

    }

    public void function(){
        System.out.println("Hello World");
    }

    public static synchronized Singleton  getInstance(){
        if (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}
你会发现,改进后的代码和之前的唯一区别就是在getInstance()函数前面 synchronized 关键字。这样,就对这个代码段加锁,这样在A访问的时候,B就不会访问到,程序就不会出错。但是这样很影响效率。

3.饿汉式(在类加载时就初始化)

饿汉式和懒汉式这种命名方式的最大的区别是在于,如果你只在成员变量里面声明了singleton而未赋值,这就是懒汉。

public class Singleton {
    public static Singleton singleton = new Singleton();

    private Singleton(){

    }

    public void function(){
        System.out.println("Hello World");
    }

    public static Singleton  getInstance(){
        return singleton;
    }
}

这样虽然也是多线程安全的,因为在类加载的时候就已经初始化了一个单例,但是这样会造成资源的浪费。因为这个类的对象可能在最开始是不需要使用的。


4.DCL双重校验:

public class Singleton {
    public volatile static Singleton singleton;

    private Singleton(){

    }

    public void function(){
        System.out.println("Hello World");
    }

    public static  Singleton  getInstance(){
        if (singleton == null){
            synchronized (Singleton.class){ //进入临界区
                if(singleton == null){ //再检查一次
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
在进入临界区之前就检测一次singleton,进入临界区之后再检测一次。


P.S. 文章不妥之处还望指正



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值