【问题标题】:How to create a session scoped thread safe object instance?如何创建会话范围的线程安全对象实例?
【发布时间】:2020-02-26 02:50:10
【问题描述】:

我想在我的程序中为 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;
      }
    }
  }

我想要延迟创建对象,能够重置它。上述方法是线程安全的吗?如果没有,是否有解决此问题的通用模式?

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

【问题讨论】:

  • 虽然不一样,但它们是单例的示例,而不是能够重置状态的示例。
  • 我明白了。没有注意到你有一个reset 方法。如果这是您需要的,您可能想在问题中强调它。一般来说,单身人士是永远的。
  • 你想要一个单例还是会话范围的对象?这个重置业务让我觉得你的设计有问题。单例包含什么?

标签: java multithreading architecture synchronization


【解决方案1】:

上述方法是线程安全的吗?

不,不是。

假设我们有两个线程 - AB

A 调用getInstance(),通过instance==null 检查,然后上下文切换到B,它调用reset()。在B 执行完reset() 后,A 再次获取上下文并返回instance,现在为空。

如果没有,是否有解决此问题的通用模式?

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

我能想到更复杂的解决方案,但只有在实际分析表明您在 synchronized 块上花费了太多时间时,我才会使用它们。请注意,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 上旋转,此时它返回一个旧的 instance 值。这种情况是否可以接受取决于您。

【讨论】:

    【解决方案2】:

    上述方法是线程安全的吗?

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

    那是“线程安全的吗?”

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


    另一种看待它的方式:如果有人告诉您 是“线程安全的”,那么他们可能会告诉您多个线程对类方法的并发调用是行不通的任何不同意类文档的事情,并且不会做任何不同意理性程序员认为类在文档不是绝对清楚的情况下应该的行为方式的事情。

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

    是否每个使用您的类的人清楚地了解程序中的任何线程都不能调用reset(),而对旧单例的任何引用仍然存在?如果是这样,那么我会称其为弱设计,因为它远非“初级程序员安全”,但我不情愿地承认,从严格的语言律师的角度来看,你可以打电话给你的 @987654324 @class "线程安全。"

    【讨论】:

      猜你喜欢
      • 2012-07-06
      • 1970-01-01
      • 1970-01-01
      • 2012-07-28
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 2018-06-25
      • 1970-01-01
      相关资源
      最近更新 更多