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 中对一个 Switch Case 语句使用多个值
发布时间:2023/07/16 浏览次数:172 分类:Java
-
在本文中,我们将学习如何在一个 switch-case 语句中使用多个值。使用 switch-case 语句 Java 允许程序员通过使用 switch case 语句来像其他编程语言一样克服太多的 if-else 条件语句。
Java 聚合与组合
发布时间:2023/07/16 浏览次数:67 分类:Java
-
在Java中,聚合和组合是紧密相连的两个概念。 组合是类之间的紧密关联,而聚合是弱关联。Java 中的组合 Java 中的聚合
Java 错误 Java.Security.InvalidKeyException: Illegal Key Size
发布时间:2023/07/15 浏览次数:98 分类:Java
-
本篇文章介绍包含 java.security.InvalidKeyException: Illegal key size 的 Java 代码。 然后,我们将了解其可能的原因。最后,它通过消除指定的错误来引导我们找到解决方案。
Java 错误 Java.SQL.SQLException: Access Denied for User Root@Localhost
发布时间:2023/07/15 浏览次数:72 分类:Java
-
本篇文章介绍如何解决 Java 中的 java.sql.SQLException: Access Denied for user 'root'@'localhost' 错误。修复 Java 中的 java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
Java 异常 Java.Lang.ClassNotFoundeException: Sun.Jdbc.Odbc.JdbcOdbcDriver
发布时间:2023/07/15 浏览次数:106 分类:Java
-
本篇文章介绍了 Java 中的 java.lang.ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver 错误。修复 Java 中的 java.lang.ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver
Java 错误 Gateway Process Exited Before Sending Its Port Number
发布时间:2023/07/15 浏览次数:104 分类:Java
-
本篇文章介绍了 Java 中 Java gateway process exited before sending the driver its port number 错误 Java gateway process exited before sending the driver its port number 错误
修复 Java 中 Java.Net.BindException: Address Already in Use: Bind 错误
发布时间:2023/07/15 浏览次数:77 分类:Java
-
本篇文章介绍了 Java 中的 java.net.BindException:Address already in use: Bind 错误。修复Java 中的 java.net.BindException:Address already in use: Bind
修复 Java 中 Java.Net.SocketException: Broken Pipe 错误
发布时间:2023/07/15 浏览次数:162 分类:Java
-
本篇文章介绍了使用 Java 编程的 java.net.SocketException: Broken pipeline 错误,并重点介绍了其可能的原因和解决方案。错误描述、原因及解决方法
Java 异常 Java.Lang.ClassNotFoundException: Org.SpringFramework.Web.Servlet.Dis
发布时间:2023/07/15 浏览次数:179 分类:Java
-
今天关于 Java 的文章将介绍错误 java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet。什么是 java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet