创新设计模式:单例模式

单例设计模式是一种软件设计模式,用于将类的实例化限制为一个对象。
与其他创建设计模式(例如抽象工厂工厂构建器模式)相比,单例将创建一个对象,但也将负责,因此该对象只有一个实例存在。

当创建一个单例类时,必须解决一些特定问题。

  • 如何确保一个类只有一个实例。
  • 如何轻松访问类的唯一实例
  • 类如何控制其实例化
  • 如何限制一个类的实例数

假设我们有一个发送消息的类。
Messenger类。

package com.gkatzioura.design.creational.singleton;

public class Messenger {

    public void send(String message) {
        
    }
}

但是,我们希望消息过程仅由Messenger类的一个实例处理。 想象一下Messenger类打开一个tcp连接(例如xmpp)并且必须保持该连接活动以便发送消息的情况。 每次我们必须发送一条消息时,打开一个新的xmpp连接都是非常低效的。

因此,我们将继续使Messenger类成为单例。

package com.gkatzioura.design.creational.singleton;

public class Messenger {

    private static Messenger messenger = new Messenger();

    private Messenger() {}

    public static Messenger getInstance() {
        return messenger;
    }

    public void send(String message) {

    }
}

如您所见,我们将Messenger构造函数设置为私有,并使用静态变量初始化了Messenger。
静态变量是类级别的变量,仅当将类加载到内存中时,内存分配才发生一次。 通过这种方式,我们确保将使Messenger类仅实例化一次。 一旦被调用,getInstance方法将获取静态Messenger实例。

显然,以前的方法有其优点和缺点。 我们不必担心线程安全,仅在加载Messenger类时才创建实例。 但是,它缺乏灵活性。 考虑将配置变量传递给Messenger构造函数的场景。 使用以前的方法是不可能的。

一种解决方法是在getInstance方法上实例化Messenger类。

package com.gkatzioura.design.creational.singleton.lait;

public class Messenger {

    private static Messenger messenger;

    private Messenger() {}

    public static Messenger getInstance() {

        if(messenger==null) {
            messenger = new Messenger();
        }

        return messenger;
    }
    
    public void send(String message) {

    }
}

上面的方法在某些情况下可能有效,但是在类可能在多线程环境中实例化的情况下,它会丢失线程安全性。

使我们的类线程安全的最简单方法是同步getInstance方法。

package com.gkatzioura.design.creational.singleton.lait;

public class Messenger {

    private static Messenger messenger;

    private Messenger() {}

    public synchronized static Messenger getInstance() {

        if(messenger==null) {
            messenger = new Messenger();
        }

        return messenger;
    }

    public void send(String message) {

    }
}

那将工作。 至少将使Messenger的创建同步,并且不会创建任何重复项。 这种方法的问题在于,在创建对象时仅需要同步一次。 使用上面的代码将导致不必要的开销。

另一种方法是使用双重检查锁定方法。 现在,经过双重检查的锁定需要格外小心,因为很容易从正确的选择中找出损坏的实现
最好的方法是使用volatile关键字实现延迟加载。

package com.gkatzioura.design.creational.singleton.dcl;

public class Messenger {

    private static final Object lock = new Object();
    private static volatile Messenger messenger;

    private Messenger() {}

    public static Messenger getInstance() {

        if(messenger==null) {
            synchronized (lock) {
                if(messenger==null) {
                    messenger = new Messenger();
                }
            }
        }

        return messenger;
    }

    public void send(String message) {

    }
}

通过使用volatile关键字,我们可以防止对volatile的写入相对于任何先前的读取或写入进行重新排序,并防止对volatile的读取相对于随后的任何读取或写入进行重新排序。 互斥对象也用于实现同步。

总而言之,我们创建了一个对象,并确保该对象只有一个实例。 我们还确保在多线程环境中实例化对象不会有任何问题。

您可以在github上找到源代码。

在下一篇博客文章中,我们将介绍原型模式。

另外,我还编写了备忘单,其中包含“创作设计模式”的摘要。 在链接中注册以接收它。

翻译自: https://www.javacodegeeks.com/2018/03/creational-design-patterns-singleton-pattern.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值