我希望在我的程序中为线程安全的 session 提供一个可重置对象实例, session 的一个示例可能是登录的用户 session 。
我目前正在做这样的事情;
public final class ObjectFactory {
private static volatile NativeObjectWrapper instance = null;
private Singleton() {}
public static NativeObjectWrapper getInstance() {
if (instance == null) {
synchronized(ObjectFactory.class) {
if (instance == null) {
instance = new NativeObjectWrapper(AuthData);
}
}
}
return instance;
}
public void reset() {
synchronized(ObjectFactory.class) {
instance = null;
}
}
}
我想要延迟创建对象,能够重置它。上述方法是线程安全的吗?如果没有,是否有通用模式来解决这个问题?
再举一个例子,这里的作用域对象有一些基于用户 session 的内部数据,因此应该是每个用户 session 的一个新实例。
最佳答案
Is the above approach threadsafe?
不,不是。
假设我们有两个线程 - A
和 B
。
A
调用 getInstance()
,通过 instance==null
检查,然后上下文切换到 B
,后者调用 reset()
。 B
执行完 reset()
后,A
再次获取上下文并返回 instance
,现在该实例为 null。
if not is there a common pattern to solve this?
我不记得见过带有 reset
方法的单例,所以我不知道此问题的任何常见模式。不过,最简单的解决方案是删除 getInstance()
中的第一个 if (instance == null)
检查。这将使您的实现线程安全,因为 instance
始终在同步块(synchronized block)内检查和修改。在这种情况下,您还可以从实例
中删除 volatile
修饰符,因为它始终是从同步块(synchronized block)内访问的。
我可以想到更复杂的解决方案,但只有当现实世界的分析表明您在同步
block 上花费了太多时间时,我才会使用它们。请注意,JVM 有 some sophisticated ways避免使用“真正的”锁来最大限度地减少阻塞。
一种更棘手的方法可能是只读取 instance
字段一次:
public static Singleton getInstance() {
Singleton toReturn = instance;
if (toReturn == null) {
synchronized(SingletonFactory.class) {
if (instance == null) {
instance = new Singleton();
toReturn = instance;
}
}
}
return toReturn ;
}
但这可能会导致返回旧的“实例”。例如,一个线程可以执行 Singleton toReturn = instance
并获得一个有效的实例,然后失去 CPU。此时,1000 个其他线程可以创建并重置 1000 个其他实例,直到原始线程再次在 CPU 上旋转,此时它返回旧的实例值。这种情况是否可以接受取决于您。
关于java - 如何创建 session 范围的线程安全对象实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58631569/