浅谈设计模式之单例模式

单例模式是三种创建型设计模式之一,基本是设计模式里面最简单的一种了,面试时一般被问到在项目里用过什么设计模式,基本都会说到单例。

单例模式的定义是一个类只会创建一个实例,那我们就可以将其设计为单例。

单例的实现也很简单,只要思考以下几个条件即可:

  • 构造函数设为private,让外部不能用new关键字创建实例
  • 创建时是否线程安全
  • 创建时是否允许懒加载
  • 获取实例时是否高效(getIntance()方法是否有性能问题)

然后我们就上面几个问题开始思考,整个推论过程也就有了饿汉式,懒汉式,双从检测等方式。

饿汉式

首先我们很容易想到饿汉式,直接在一个类里面定义初始化好的一个静态成员变量,使用时直接返回即可。

public class SingletonDemo {

    private final static SingletonDemo instance = new SingletonDemo();

    private SingletonDemo() {
    }

    public static SingletonDemo GetInstance() {
        return instance;
    }

}

但是饿汉式有个问题如果实例很大,但一开始又用不到则没必要程序启动时候就创建,所以一般是做成懒加载(在需要用到的时候在创建)形式。

懒汉式

于是就有了下面的懒汉式。

public class SingletonDemo {

    private static SingletonDemo instance;

    private SingletonDemo() {
    }

    public static synchronized SingletonDemo GetInstance() {
        if (instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

}

懒汉式问题是用了一把大锁将整个方法锁住,保持线程安全,思路没问题,但是实现不太好,因为锁的粒度太大了,会造成很多不必要的等待,这种写法即使是已经创建了实例,多个线程访问还是要排队,这明显是不可接受的。所以我们需要对锁进行优化。

双重检测
public class SingletonDemo {

    private static SingletonDemo instance;

    private SingletonDemo() {
    }

    public static SingletonDemo GetInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class){
                if (instance==null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

}

上面这块代码一开始看可能会觉得很奇怪,为啥没事去判断两次?感觉毫无意义。

但是当你想通了其实觉得还是有必要的,第二次判空主要是避免重复创建。

第一次判空是正常判空,这时候锁住类,排队的多个线程会有一个进入同步块,这时候实例为空,开始创建实例。当创建完实例后,线程会释放锁,下一个线程会进去,这时候如果不判断instance为空,那另一个线程会重新创建实例覆盖上一个实例。这会造成实例重复创建,违背了单例的初衷,另一个如果静态代码块如果使用的是this而不是类锁就会完全锁不住,因为实例不一样了。

双重检测虽然解决了多线程的冲突,但是有个很大问题是代码可读性很差,正常人一眼根本看不懂,是否有比较优雅的实现呢?

当然,请往下看。

静态内部类
public class SingletonDemo {


    private SingletonDemo() {
    }

    private static class SingletonDemoHolder{
        private static final SingletonDemo instance = new SingletonDemo();
    }

    public static SingletonDemo GetInstance() {
        return SingletonDemoHolder.instance;
    }

}

通过在单例类里面创建一个静态内部类,利用jvm的类加载特性,当外面的SingletonDemo被创建时并不会加载里面的SingletonDemoHolder类,只有当GetInstance()方法被调用时才会被加载,并创建instance实例。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。

当然,还有另一种比较简单的实现,枚举。

枚举
public enum SingletonDemo1 {

    INTANCE;


}

枚举是effective java比较推荐的一种单例实现方式,但是枚举这个我用的比较少,不做过多解释,有兴趣的朋友可以自行查看。

类比

我们知道IOC容器有自动创建bean的功能,里面有个字段scope代表创建类型,默认的是singleton,这块其实就是采用了单例的思想。但需要注意如果每次要新建bean的话需要将scope值改为prototype。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值