单例设计模式是一种软件设计模式,用于将类的实例化限制为一个对象。
与其他创建设计模式(例如抽象工厂 , 工厂和构建器模式)相比,单例将创建一个对象,但也将负责,因此该对象只有一个实例存在。
当创建一个单例类时,必须解决一些特定问题。
- 如何确保一个类只有一个实例。
- 如何轻松访问类的唯一实例
- 类如何控制其实例化
- 如何限制一个类的实例数
假设我们有一个发送消息的类。
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