Java 中的线程安全延迟初始化
本文将讨论在 Java 中实现线程安全的延迟初始化。
Java 中的对象初始化
延迟初始化是延迟对象创建的行为。 它还可能导致某些计算任务或首次昂贵流程的延迟。
Java 中有两种类型的对象初始化。 它们是急切初始化和惰性初始化。
在 Eager 初始化中,对象初始化发生在编译时。 如果过程很长,这会消耗时间和内存。
在延迟初始化中,对象初始化在程序需要时发生。 因此它可以节省内存、增强处理能力并提高效率。
线程是可以并行运行进程以优化时间的次要组件。 线程安全是一个 Java 类,可确保类的内部状态和函数在多个线程同时调用期间返回的值的正确性。
在 Java 中实现延迟初始化
getter 方法检查私有成员是否已经具有某些值。 如果有,该函数返回它; 否则,它会创建一个新实例并在第一次执行时返回它。
有两种方法可以进行延迟初始化。
- 在函数名称之前使用synchronize 关键字。
- 使用双重检查锁定或同步块方法。
在 Java 中编写延迟初始化的线程安全代码
当多个线程并行运行时,变量可能会返回意外结果。 这里,我们需要一个线程安全的代码来防止Java中的死锁。
例子:
考虑一家银行,其中只有一个代币计数器可用于代币分配。 这里的任务是仅当客户进来时创建一个 Bank 类; 该过程是惰性初始化。
让我们创建多个线程,例如 t1、t2 等,以了解多线程调用。 每个线程都是一个客户。
当客户来获取代币时,我们创建一个 Bank 对象,并在函数参数中使用代币编号调用 operation()
。 现在,显示令牌是否可用并指定消息中的下一个操作。
如果有token,则发行token; 否则,请继续接待下一位顾客。
该程序解释了 getInstanceSynchronizedWay()
和 getInstanceSynchronizedBlockWay()
。 我们可以使用Thread.sleep来保证输出的准确性。
在Java中使用synchronized方法进行线程安全的延迟初始化
在此示例中,我们有两个客户线程。
_instance
变量检查实例是否为空。 如果变量为空,程序将创建一个实例。
空标志检查令牌是否可用。 如果令牌可用,则标志值为 true。
将令牌提供给客户后,程序将标志值设置为 false。 因此,在 operation()
完成之前,下一个客户将不会获得令牌。
示例代码:
//Helper class as a Singleton Class
class BankOperation {
//Private variables
private static BankOperation _instance;
private boolean empty = false;
private String customerName = "default";
// Displays the instance only during creation
private BankOperation()
{
System.out.println("Instance Creation Over\n");
}
//synchronized method
public static synchronized BankOperation
getInstanceSynchronizedWay()
{
if (_instance == null)
_instance = new BankOperation();
return _instance;
}
//Check if the token is available
public boolean isOperationBankEmpty()
{
return empty;
}
//When token giving is successful
public void endOperation() {
empty = true;
}
//Multiple threads access the method below
public synchronized void operation(String paramCust)
{
//When the token is available. The flag is true.
if (empty == true) {
customerName = paramCust;
//Issue the token to the customer
System.out.println("Operation - Token is available.\n" + "Giving token to - " + customerName);
empty = false;
}
//The token is not available
else {
System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
}
}
}
//Main class
public class Bank {
//Driver function
public static void main(String args[])
{
//synchronized method
//Create a thread in main()
Thread t1 = new Thread(new Runnable() {
//run() for thread 1
public void run()
{
//Create objects of other classes here
BankOperation i1 = BankOperation.getInstanceSynchronizedWay();
System.out.println("Synchronized Method - Instance 1 - " + i1 + "\n");
//The method with an argument
i1.endOperation();
i1.operation("Customer 1");
}
});
//Thread 2
Thread t2 = new Thread(new Runnable() {
//run() for thread 2
public void run()
{
BankOperation i2 = BankOperation.getInstanceSynchronizedWay();
System.out.println("Synchronized Method - Instance 2 - " + i2 + "\n");
i2.operation("Customer 2");
}
});
// Starting thread 1
t1.start();
//Start thread 2
t2.start();
}
}
输出:
Instance Creation Over
Synchronized Method - Instance 1 - BankOperation@792bbbb1
Synchronized Method - Instance 2 - BankOperation@792bbbb1
Operation - Token is available.
Giving the token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1
在上面的输出中,用户可以观察到两个客户实例是并行出现的。 因此,第一个实例获取令牌,第二个实例获取消息。
在 Java 中使用双重检查锁定方法进行线程安全的延迟初始化
在此示例中,我们创建两个客户线程。 _instanceForDoubleCheckLocking
变量两次检查实例是否为 null,并且方法 getInstanceSynchronizedBlockWay()
返回新实例。
标志empty的值决定了令牌的可用性。 如果该标志为真,则该令牌可供客户使用。
客户获得令牌后,该标志变为 false。 在当前线程完成其操作之前,运行多个线程无法更改任何值。
示例代码:
//Helper class
class BankOperation {
//Private variable declaration
private static BankOperation _instanceForDoubleCheckLocking;
private boolean empty = false;
private String customerName = "default";
private BankOperation()
{
System.out.println("Instance Creation Over\n");
}
//Synchronized Block Method or Double-Checked Locking
public static BankOperation
getInstanceSynchronizedBlockWay()
{
//Check double locking
if (_instanceForDoubleCheckLocking == null)
synchronized (BankOperation.class)
{
if (_instanceForDoubleCheckLocking == null)
_instanceForDoubleCheckLocking = new BankOperation();
}
return _instanceForDoubleCheckLocking;
}
//The `token` availability check
public boolean isOperationBankEmpty()
{
return empty;
}
//After giving the token set the flag value
public void endOperation() {
empty = true;
}
//Multiple threads access the method below
public synchronized void operation(String paramCust)
{
//The flag is true when the `token' is available
if (empty == true) {
customerName = paramCust;
//Give the `token` to the customer
System.out.println("Operation - Token is available.\n" + "Giving token to - " + customerName);
empty = false;
}
//When the `Token` is not available
else {
System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
}
}
}
//Main class
public class Bank {
//Driver function
public static void main(String args[])
{
//Double Checked Locking
System.out.println("Double Checked locking - Synchronized Block");
//Thread 3
Thread t3 = new Thread(new Runnable() {
//run() for thread 3
public void run()
{
BankOperation i1 = BankOperation.getInstanceSynchronizedBlockWay();
System.out.println("Double Checked Locking - Instance 1 - " + i1 + "\n");
i1.endOperation();
i1.operation("Customer 1");
}
});
//Thread 4
Thread t4 = new Thread(new Runnable() {
//run() for thread 4
public void run()
{
BankOperation i2 = BankOperation.getInstanceSynchronizedBlockWay();
System.out.println("Double Checked Locking - Instance 2 - " + i2 + "\n");
i2.operation("Customer 2");
}
});
t3.start();
t4.start();
}
}
输出1:
Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 1 - BankOperation@1efc89d6
Double Checked Locking - Instance 2 - BankOperation@1efc89d6
Operation - Token is available.
Giving token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1
请注意,当两个客户实例并行到达时,程序将令牌提供给第一个实例并通知第二个客户。
输出2:
Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 2 - BankOperation@282416b6
Double Checked Locking - Instance 1 - BankOperation@282416b6
Sorry Customer 2, the counter closed with the default
Operation - Token is available.
Giving Bank token to - Customer 1
这里,token对于客户2来说是不可用的,原因是在 operation()
之前缺少 endOperation()
。 客户 1 获得令牌,因为 endOperation()
在 operation()
之前运行。
本教程教给我们两种在 Java 中实现线程安全延迟初始化的方法。 第一种方法使用 synchronized 关键字,第二种方法是 synchronized 块方法。
同步块方法确保了双重检查。 用户可以根据上下文选择任何一种方式。
相关文章
如何在 Java 中延迟几秒钟的时间
发布时间:2023/12/17 浏览次数:217 分类:Java
-
本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。
如何在 Java 中把 Hashmap 转换为 JSON 对象
发布时间:2023/12/17 浏览次数:187 分类:Java
-
它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。
如何在 Java 中按值排序 Map
发布时间:2023/12/17 浏览次数:171 分类:Java
-
本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map
进行排序,并列出了一些示例代码来理解它。
如何在 Java 中打印 HashMap
发布时间:2023/12/17 浏览次数:192 分类:Java
-
本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。
在 Java 中更新 Hashmap 的值
发布时间:2023/12/17 浏览次数:146 分类:Java
-
本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。
Java 中的 hashmap 和 map 之间的区别
发布时间:2023/12/17 浏览次数:79 分类:Java
-
本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,
在 Java 中获取用户主目录
发布时间:2023/12/17 浏览次数:218 分类:Java
-
这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。
Java 中 size 和 length 的区别
发布时间:2023/12/17 浏览次数:179 分类:Java
-
这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。
Java 中的互斥锁
发布时间:2023/12/17 浏览次数:111 分类:Java
-
了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,