Singleton 是最广泛使用的创意设计模式之一,用于限制应用程序创建的对象。如果您在多线程环境中使用它,那么 singleton 类的线程安全性非常重要。在现实世界的应用中,数据库连接或企业信息系统(EIS)等资源有限,应该明智地使用以避免任何资源崩溃。为了实现这一点,我们可以实施 [Singleton 设计模式](/community/tutorials/java-singleton-design pattern-best-practices-examples)。
在 Java 中使用 Thread Safe Singleton
In general, we follow the below steps to create a singleton class:
- 创建私有 constructor以避免使用新操作员创建任何新对象
- 声明具有相同类型的私有 static实例
- 提供一个返回 singleton 类实例变量的公共静态方法。
使用上面的步骤,我创建了一个单曲类,看起来像下面。
1package com.journaldev.designpatterns;
2
3public class ASingleton {
4
5 private static ASingleton instance = null;
6
7 private ASingleton() {
8 }
9
10 public static ASingleton getInstance() {
11 if (instance == null) {
12 instance = new ASingleton();
13 }
14 return instance;
15 }
16
17}
在上面的代码中, getInstance() 方法不是线程安全的。 多个线程可以同时访问它. 对于最初的几个线程,当实例变量没有初始化时,多个线程可以进入 if 循环并创建多个实例。
如何在Singleton Class中实现线程安全?
有三种方法可以实现线条安全。
- ** 在类加载时创建实例变量.**
:
- 无同步的线程安全
- 易于实施
:
- 可能不会在应用程序中使用的资源的早期创建
- 客户端应用程序无法传递任何论点,所以我们无法重复使用它. 例如,有一个通用单一类的数据库连接,客户端应用程序提供数据库服务器属性
- ** 同步 getInstance() 方法 **.
:
- 线程安全保证
- 客户端应用程序可以通过参数
- 实现了缓慢的初始化
:
- 由于锁定过头导致的性能较慢
- 一旦实例变量初始化后不需要的无需同步
- ** 使用 if 循环和波动变量中的同步区块**
:
- 线程安全得到保证
- 客户端应用程序可以传输参数
- 实现了缓慢的初始化
- 同步过头是最小的,只有当变量为 null 时,第一批线程才适用。
:
附加如果条件
考虑到实现线程安全的所有三种方法,我认为第三种是最好的选择,在这种情况下,修改后的类将看起来像这样:
1package com.journaldev.designpatterns;
2
3public class ASingleton {
4
5 private static volatile ASingleton instance;
6 private static Object mutex = new Object();
7
8 private ASingleton() {
9 }
10
11 public static ASingleton getInstance() {
12 ASingleton result = instance;
13 if (result == null) {
14 synchronized (mutex) {
15 result = instance;
16 if (result == null)
17 instance = result = new ASingleton();
18 }
19 }
20 return result;
21 }
22
23}
局部变量结果
似乎是不必要的,但它是为了改善我们的代码的性能。在实例已经初始化的情况下(大多数情况下),波动字段只能访问一次(由于回归结果;
而不是回归实例;
)。
奖金类型
String不是一个非常好的候选人,可以用同步的关键字。 这是因为它们存储在一个 string pool中,我们不想锁定一个可能被另一个代码使用的字符串。 所以我正在使用一个 Object 变量。 了解更多关于同步和 java 中的线程安全。
您可以从我们的 GitHub 存储库中查看更多 Java 示例。