Java Singleton类中的线程安全
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关键字一起使用的候选对象。
这是因为它们存储在字符串池中,我们不想锁定可能被另一段代码使用的字符串。
所以我正在使用对象变量。