Java Singleton类中的线程安全

时间:2020-02-23 14:37:27  来源:igfitidea点击:

Singleton是使用最广泛的创建设计模式之一,用于限制应用程序创建的对象。
如果在多线程环境中使用它,那么单例类的线程安全性非常重要。

在现实世界的应用程序中,数据库连接或者企业信息系统(EIS)之类的资源是有限的,应该明智地使用以避免任何资源紧张。

为此,我们可以实现Singleton设计模式。
我们可以为资源创建一个包装器类,并将运行时创建的对象数限制为一个。

Java中的线程安全单例

通常,我们按照以下步骤创建单例类:

  • 创建私有构造函数,以避免使用new运算符创建任何新对象。

  • 声明同一类的私有静态实例。

  • 提供一个公共静态方法,该方法将返回单例类实例变量。
    如果未初始化变量,则将其初始化,否则只需返回实例变量。

使用以上步骤,我创建了一个单例类,如下所示。

ASingleton.java

package com.theitroad.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循环并创建多个实例。
它将破坏我们的单例实现。

如何在Singleton类中实现线程安全?

我们可以通过三种方法来实现线程安全。

  • 在类加载时创建实例变量。

优点:

  • 没有同步的线程安全
  • 易于实施

缺点:

  • 尽早创建可能不在应用程序中使用的资源。

  • 客户端应用程序无法传递任何参数,因此我们无法重复使用它。
    例如,在客户端应用程序提供数据库服务器属性的情况下,具有用于数据库连接的通用单例类。

  • 同步getInstance()方法。

优点:

  • 线程安全得到保证。

  • 客户端应用程序可以传递参数

  • 实现了延迟初始化

缺点:

  • 由于锁定开销而导致性能降低。

  • 实例变量初始化后就不需要进行不必要的同步。

  • 在if循环和volatile变量内使用同步块

优点:

  • 线程安全得到保证
  • 客户端应用程序可以传递参数
  • 实现了延迟初始化
  • 同步开销最小,并且仅在变量为null时才适用于前几个线程。

缺点:

  • 如果条件另外

从实现线程安全的所有三种方式来看,我认为第三种是最佳选择。
在这种情况下,修改后的类将如下所示:

package com.theitroad.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似乎是不必要的。
但是,它可以改善我们的代码的性能。
如果实例已经初始化(大多数情况下),则volatile字段仅被访问一次(由于"返回结果;"而不是"返回实例;")。
这样可以将方法的整体性能提高多达25%。

如果您认为有更好的方法可以实现此目的,或者在上述实现中线程安全性受到损害,请发表注释并与我们所有人共享。

说明

字符串不是非常适合与synced关键字一起使用的候选对象。
这是因为它们存储在字符串池中,我们不想锁定可能被另一段代码使用的字符串。
所以我正在使用对象变量。