设计模式之单例模式

单例模式定义为某个类在系统中只能有一个实例。

这也是我当初认为是最简单的设计模式。

常用写法

以前一般写单例模式都是写成如下代码,

public class SmsInstance {

    private final static SmsInstance smsInstance = new SmsInstance();

    private SmsInstance(){
    }

    public static SmsInstance getSmsInstance(){
        return smsInstance;
    }
}

这种是饿汉式单例,就是先创建一个对象出来,需要的时候就返回这个对象,还有懒汉式的,在需要的时候才创建对象,如下所示。

private static SmsInstance smsInstance = null;

    private SmsInstance(){
    }

    public static SmsInstance getSmsInstance() {
        if (smsInstance == null) {
            smsInstance = new SmsInstance();
        }
        return smsInstance;
    }

但是考虑一点,为什么需要单例,单例就是你不想创建多个实例,只想系统中拥有一个实例。

首先,上面两种都可以通过反射创建实例,如下所示,获取到构造函数,然后设置setAccessible(true)即可。

		Class clazz = SmsInstance.class;
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            constructor.setAccessible(true);
            Object o = constructor.newInstance(null);
            if(o instanceof SmsInstance){
                SmsInstance s = (SmsInstance)o;
                System.out.println(s);
            }

            Object o2 = constructor.newInstance(null);
            if(o2 instanceof SmsInstance){
                SmsInstance s = (SmsInstance)o2;
                System.out.println(s);
            }
        }

当然应该没有人会丧心病狂到这种程度,放着好好的getSmsInstance不用,非要用反射newInstance出来。

而对于懒汉式,其实不通过反射也可以创建多个实例。

if (smsInstance == null) {
            smsInstance = new SmsInstance();
        }

分析懒汉式的代码,会发现有一个判空。

加入thread1判断为空,正好要new对象时,cpu时间到了,于是切换为thread2执行,此时thread2 new了对象出来后轮到thread1执行,thread1就会继续暂停之前的操作,new一个对象。

不过这种情况也很少发生,即使发生了有时候也没什么关系,只要保证多个实例对系统没影响就行。

线程安全的丧心病狂写法

public static synchronized SmsInstance getSmsInstance() {
        if (smsInstance == null) {
            smsInstance = new SmsInstance();
        }
        return smsInstance;
    }

不过项目组中有些人为了保证只有一个实例,加了synchronized属性在方法上,这样虽然保证了线程安全,但是效率变慢了啊,线程需要排队了,真更加丧心病狂。

还有一种是双null检查,如下,使用volatile修饰对象,然后判断两次空,这样不会有任何线程安全问题。

private static volatile SmsInstance smsInstance;

    private SmsInstance() {
    }

    public static synchronized SmsInstance getSmsInstance() {
        if (smsInstance == null) {
            synchronized (SmsInstance.class) {
                if (smsInstance == null)
                    smsInstance = new SmsInstance();
            }

        }
        return smsInstance;
    }

静态内部类写法

    private SmsInstance() {
    }

    public static SmsInstance getSmsInstance() {
        return SmsInnerInstance.instance;
    }

    private static class SmsInnerInstance{
        private final static SmsInstance instance = new SmsInstance();
    }

这种是为了应对上面双Null检查的改写版,为了去掉线程可能排队的情况。

并不是太想用synchronized这个修饰词。

总结

单例模式,保证只有一个实例即可,有多个实例就想会不会影响系统执行,如果多个实例很少发生,也不会影响系统执行,那么简单的常用写法即可。

看过很多为了单例而写了各种判断的代码,其实大可不必,虽然看着牛逼,但很多人基本一眼看明白的就是常用写法。

上面无论是工厂模式、建造者模式还是单例模式,都是为了创建对象而存在,可以归类为创建型设计模式。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值