Java 如何创建会话范围的线程安全对象实例?

Java 如何创建会话范围的线程安全对象实例?,java,multithreading,architecture,synchronization,Java,Multithreading,Architecture,Synchronization,我想在程序中为线程安全的会话创建一个可重置的对象实例,会话的一个示例可能是登录的用户会话 我现在正在做这样的事情 public final class ObjectFactory { private static volatile NativeObjectWrapper instance = null; private Singleton() {} public static NativeObjectWrapper getInstance() {

我想在程序中为线程安全的会话创建一个可重置的对象实例,会话的一个示例可能是登录的用户会话

我现在正在做这样的事情

  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;
      }
    }
  }
我想让对象以惰性方式创建,能够重置它。上述方法是线程安全的吗?如果没有,是否有一个共同的模式来解决这个问题

再次举一个例子,这里的作用域对象有一些基于用户会话的内部数据,因此每个用户会话都应该是一个新实例

上述方法是线程安全的吗

不,不是

假设我们有两个线程-
A
B

A
调用
getInstance()
,通过
instance==null
检查,然后有一个到
B
的上下文开关,它调用
reset()
。在
B
完成执行
reset()
后,
A
再次获取上下文并返回
实例,该实例现在为空

如果没有,是否有一个共同的模式来解决这个问题

我不记得用
reset
方法查看过单例,所以我不知道这个问题的任何常见模式。但是,最简单的解决方案是,如果(instance==null)
签入
getInstance()
,则只删除第一个
。这将使您的实现线程安全,因为
实例
总是在同步块中进行检查和修改。在这种情况下,还可以从
实例
中删除
volatile
修饰符,因为它总是从同步块中访问

我可以想到更复杂的解决方案,但只有当现实世界的评测显示您在
同步
块上花费了太多时间时,我才会使用它们。注意,JVM必须避免使用“实”锁来最小化阻塞

一种更棘手的方法是只读取一次
实例
字段:

public static Singleton getInstance() {
    Singleton toReturn = instance;
    if (toReturn == null) {
        synchronized(SingletonFactory.class) {
            if (instance == null) {
                instance = new Singleton();
                toReturn = instance;
            }
        }
    }

    return toReturn ;
}
但这可能导致返回一个旧的“实例”。例如,一个线程可以执行
Singleton to返回=instance
并获得一个有效实例,然后失去CPU。此时,1000个其他线程可以创建并重置1000个其他实例,直到原始线程再次在CPU上旋转,此时它返回一个旧的
实例
值。这种情况是否可以接受由你决定

上述方法是线程安全的吗

不,不是

假设我们有两个线程-
A
B

A
调用
getInstance()
,通过
instance==null
检查,然后有一个到
B
的上下文开关,它调用
reset()
。在
B
完成执行
reset()
后,
A
再次获取上下文并返回
实例,该实例现在为空

如果没有,是否有一个共同的模式来解决这个问题

我不记得用
reset
方法查看过单例,所以我不知道这个问题的任何常见模式。但是,最简单的解决方案是,如果(instance==null)
签入
getInstance()
,则只删除第一个
。这将使您的实现线程安全,因为
实例
总是在同步块中进行检查和修改。在这种情况下,还可以从
实例
中删除
volatile
修饰符,因为它总是从同步块中访问

我可以想到更复杂的解决方案,但只有当现实世界的评测显示您在
同步
块上花费了太多时间时,我才会使用它们。注意,JVM必须避免使用“实”锁来最小化阻塞

一种更棘手的方法是只读取一次
实例
字段:

public static Singleton getInstance() {
    Singleton toReturn = instance;
    if (toReturn == null) {
        synchronized(SingletonFactory.class) {
            if (instance == null) {
                instance = new Singleton();
                toReturn = instance;
            }
        }
    }

    return toReturn ;
}
但这可能导致返回一个旧的“实例”。例如,一个线程可以执行
Singleton to返回=instance
并获得一个有效实例,然后失去CPU。此时,1000个其他线程可以创建并重置1000个其他实例,直到原始线程再次在CPU上旋转,此时它返回一个旧的
实例
值。这种情况是否可以接受由你决定

上述方法是线程安全的吗

答案取决于您认为“线程安全”的含义。
reset()
方法中没有任何内容可以阻止以前调用
getInstance()
的线程继续使用旧实例

这是“线程安全”吗

一般来说,“线程安全”意味着一个线程的操作永远不会导致其他线程看到处于不一致或无效状态的共享数据。但“不一致”或“无效”的含义取决于共享数据的结构(即,应用程序的设计)


从另一个角度来看:如果有人告诉你一个类是“线程安全的”,那么他们可能会告诉你多个线程对该类方法的并发调用不会做任何与类文档不一致的事情,在文档不是绝对清晰的情况下,不会做任何与Reaceable程序员认为类应该如何行为不一致的事情

注意:这是一个较弱的“线程安全”定义,因为它掩盖了一个事实,即使用线程安全组件构建系统并不能保证系统本身是线程安全的

使用您的类的每个人都清楚地了解,当对旧单例的引用仍然存在时,程序中的任何线程都不能调用
reset()
?如果是这样的话,那么我会称之为弱设计,因为它远远不是“初级程序员安全的”,但我不情愿地承认,从严格的语言法来看