常用设计模式--单例模式

单例模式

概述

单例模式确保一个类只有一个实例,并提供一个全局访问点

我们常常希望某个对象实例只有一个,不想要频繁的创建和销毁对象,浪费系统资源,最常见的就是IO,数据库的连接,redis连接等对象,完全没有必要创建多个一模一样的对象,一个足矣。

类图:

enter image description here

正如上面类图所示,单例模式就是这么简单,用静态变量instance将实例保存起来,然后调用getInstance()时直接返回instance变量,这就是单例模式。

虽然单例模式简单,但是也不要小看它,其中还有许多细节需要我们注意。

线程安全问题

单例模式代码如下:

public class SingleObject {

    private static SingleObject instance;

    private SingleObject(){}

    public static SingleObject getInstance(){
        if(instance == null){
            instance = new SingleObject();
        }
        return instance;
    }
}

但是上面代码会有一个问题,当多个线程同时调用 getInstance() 方法时,可能会产生多个instance实例,因此这种方式并不是真正的单例。

为了解决线程安全问题,我们只需要在getInstance() 方法上使用synchronized 关键字给线程加锁即可。

public class SingleObject {

    private static SingleObject instance;

    private SingleObject(){}

    public static synchronized SingleObject getInstance(){
        if(instance == null){
            instance = new SingleObject();
        }
        return instance;
    }
}

synchronized 的作用是加锁,当多个线程同时调用getInstance() 时,只有一个线程能进入,其他线程会等待进入的线程出来之后在一一进入,这样就能保证instance 实例是唯一的。这才是真正的单例。

不过这并不是完美的解决方案,只要是锁,必然有性能损耗问题。而且对于上面的代码,其实我们只需要在线程第一次访问时加锁即可,之后并不需要锁,锁给我们带来了系统资源浪费。

所以又有了新的解决方案。上面两种方式都是在getInstance() 方法中创建实例,也就是说在要调用的时候才创建实例,这种方式被称为“懒汉式”,说实话这个词不知道谁给命名的,实在有点难听,让我总是不自觉地想到“老汉”二字。

咱们还是用它的英文名,叫 lazy loading,也就是延迟加载。

新的解决方案是not lazy loading,在类加载时就创建好了实例:

public class SingleObject {

    private static SingleObject instance = new SingleObject();

    private SingleObject(){}

    public static  SingleObject getInstance(){
        return instance;
    }
}

这种方式就可以保证实例唯一。

除了上面的几种方式,还有一种叫 double-checked locking (双重检查加锁)

这种方式主要用到两个关键字volatile 和 synchronizedsynchronized 已经解释过了,就不再多说,而volatile 关键字许多人不了解,没关系,我们先看代码:

public class SingleObject {

    private volatile static SingleObject instance;

    private SingleObject(){}

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

volatile 关键字简单来说就是可以保证instance变量在被其中一个线程new出来时,其他线程可以立即看到结果并正确的处理它。对volatile 有兴趣的朋友可以自行度娘。

这种方式的单例模式可以大大的减少锁所带来的性能损耗。

总结

综合上面所说,一般情况我们使用下面这种方式就可以了,如有特殊需求,也可以使用双重检查加锁方式(其实双重检查加锁方式笔者也没使用过)

public class SingleObject {

    private static SingleObject instance = new SingleObject();

    private SingleObject(){}

    public static  SingleObject getInstance(){
        return instance;
    }
}

优点

使用单例模式,对象在内存中只有一个实例,并且无需频繁的创建和销毁对象,大大的减少了性能的损耗

转载 :https://gitbook.cn/gitchat/column/5b1e3647294fb04d7c22b783/topic/5b1fa54952823b71110333bc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值