Singleton 是最广泛使用的创建型设计模式之一,用于限制应用程序创建的对象。如果是在多线程环境中使用,那么单例类的线程安全性就非常重要。在现实应用程序中,数据库连接或企业信息系统 (EIS) 等资源是有限的,应明智地使用以避免任何资源紧缩。为了实现这一目标,我们可以实施一个单例设计模式。我们可以创建一个包装类并将运行时创建的对象数量限制为 1。
In general, we follow the below steps to create a singleton class:
- 创建私有的构造函数以避免使用 new 运算符创建任何新对象。
- 声明私有static同一类的实例。
- 提供一个公共静态方法,该方法将返回单例类实例变量。如果变量未初始化,则对其进行初始化,否则仅返回实例变量。
使用上述步骤,我创建了一个如下所示的单例类。ASingleton.java
package com.journaldev.designpatterns;
public class ASingleton {
private static ASingleton instance = null;
private ASingleton() {
}
public static ASingleton getInstance() {
if (instance == null) {
instance = new ASingleton();
}
return instance;
}
}
在上面的代码中,getInstance()方法不是线程安全的。多个线程可以同时访问它。对于前几个线程,当实例变量没有初始化时,多个线程可以进入if循环并创建多个实例。它将破坏我们的单例实现。
我们可以通过三种方式实现线程安全。
-
在类加载时创建实例变量。
Pros:
Cons:
- 早期创建应用程序中可能不会使用的资源。
- 客户端应用程序无法传递任何参数,因此我们无法重用它。例如,具有用于数据库连接的通用单例类,其中客户端应用程序提供数据库服务器属性。
-
同步 getInstance() 方法.
Pros:
- 线程安全得到保证。
- 客户端应用程序可以传递参数
- 实现延迟初始化
Cons:
- 由于锁定开销而导致性能降低。
- 一旦实例变量初始化就不再需要不必要的同步。
-
在 if 循环和 volatile 变量中使用同步块
Pros:
- 线程安全有保障
- 客户端应用程序可以传递参数
- 实现延迟初始化
- 同步开销很小,并且仅适用于变量为空时的前几个线程。
Cons:
考虑到实现线程安全的所有三种方法,我认为第三种是最好的选择。在这种情况下,修改后的类将如下所示:
package com.journaldev.designpatterns;
public class ASingleton {
private static volatile ASingleton instance;
private static Object mutex = new Object();
private ASingleton() {
}
public static ASingleton getInstance() {
ASingleton result = instance;
if (result == null) {
synchronized (mutex) {
result = instance;
if (result == null)
instance = result = new ASingleton();
}
}
return result;
}
}
局部变量result
似乎没有必要。但是,它的存在是为了提高我们代码的性能。在实例已经初始化的情况下(大多数情况下),易失性字段仅被访问一次(由于“返回结果;”而不是“返回实例;”)。这可以将该方法的整体性能提高多达 25%。如果您认为有更好的方法来实现这一点,或者上述实现中线程安全性受到损害,请发表评论并与我们所有人分享。
String与synchronized关键字一起使用并不是一个很好的选择。这是因为它们存储在字符串池我们不想锁定可能被另一段代码使用的字符串。所以我使用一个对象变量。了解有关同步和java中的线程安全.
您可以从我们的网站查看更多 Java 示例GitHub 存储库.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)