简体   繁体   English

如何创建 session 范围线程安全 object 实例?

[英]How to create a session scoped thread safe object instance?

I want to have a resettable object instance for a session within my program that is thread safe, an example of a session might be a logged in user session. I want to have a resettable object instance for a session within my program that is thread safe, an example of a session might be a logged in user session.

I am currently doing something like this;我目前正在做这样的事情;

  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;
      }
    }
  }

I want to have the object created lazily, with the ability to reset it .我想让 object 懒惰地创建,并能够重置它 Is the above approach threadsafe?上述方法是线程安全的吗? if not is there a common pattern to solve this?如果没有,是否有解决此问题的通用模式?

An example again would be that scoped object here has some inner data based on the user session and therefore should be a new instance per user session.再举一个例子,这里的作用域 object 有一些基于用户 session 的内部数据,因此应该是每个用户 session 的新实例。

Is the above approach threadsafe?上述方法是线程安全的吗?

No, it is not.不它不是。

Say we have two threads - A and B .假设我们有两个线程 - AB

A calls getInstance() , passes the instance==null check, and then there's a context switch to B , which calls reset() . A调用getInstance() ,通过instance==null检查,然后有一个上下文切换到B ,它调用reset() After B finishes executing reset() , A gets the context again and returns instance , which is now null. B执行完reset()后, A再次获取上下文并返回instance ,现在是 null。

if not is there a common pattern to solve this?如果没有,是否有解决此问题的通用模式?

I don't remember seening singletons with a reset method, so I'm not aware of any common patterns for this problem.我不记得见过使用reset方法的单例,所以我不知道这个问题的任何常见模式。 However, the simplest solution would be to just remove the first if (instance == null) check in getInstance() .但是,最简单的解决方案是删除getInstance()中的第一个if (instance == null)检查。 This would make your implementation thread safe, as instance is always checked and modified within a synchronized block.这将使您的实现线程安全,因为始终在同步块中检查和修改instance In this scenario, you could also remove the volatile modifier from instance since it is always accessed from within a synchronized block.在这种情况下,您还可以从instance中删除volatile修饰符,因为它总是在同步块中访问。

There are more complex solutions I can think of, but I'd use them only if real-world profiling showed that you're spending too much time blocked on that synchronized block.我能想到更复杂的解决方案,但只有在实际分析表明您在synchronized块上花费了太多时间时,我才会使用它们。 Note that the JVM has some sophisticated ways of avoiding using "real" locks to minimize blocking.请注意,JVM 有一些复杂的方法可以避免使用“真正的”锁来最大限度地减少阻塞。

One trickier approach could be to read the instance field just once:一种更棘手的方法可能是只读取一次instance字段:

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

    return toReturn ;
}

But this could result in returning an old "instance".但这可能会导致返回一个旧的“实例”。 For example a thread could execute Singleton toReturn = instance and get a valid instance, then lose the CPU.例如,一个线程可以执行Singleton toReturn = instance并获得一个有效的实例,然后失去 CPU。 At this point, a 1000 other threads could create and reset 1000 other instances until the original thread gets a spin on the CPU again, at which point it returns an old instance value.此时,1000 个其他线程可以创建和重置 1000 个其他实例,直到原始线程再次在 CPU 上旋转,此时它返回一个旧instance值。 It's up to you whether such a case is acceptable.这种情况是否可以接受取决于您。

Is the above approach threadsafe?上述方法是线程安全的吗?

The answer depends on what you think "thread safe" means.答案取决于您认为“线程安全”的含义。 There is nothing in your reset() method to prevent a thread that previously called getInstance() from continuing to use the old instance.您的reset()方法中没有任何内容可以防止先前调用getInstance()的线程继续使用旧实例。

Is that "thread safe?"那是“线程安全的吗?”

Generally speaking, "thread safe" means that the actions of one thread can never cause other threads to see shared data in an inconsistent or invalid state.一般来说,“线程安全”是指一个线程的操作永远不会导致其他线程看到不一致或无效的 state 中的共享数据。 But what "inconsistent" or "invalid" mean depends on the structure of the shared data (ie, on the design of the application.)但是“不一致”或“无效”的含义取决于共享数据的结构(即,取决于应用程序的设计。)


Another way of looking at it: If somebody tells you that a class is "thread safe," then they're probably telling you that concurrent calls to the class's methods by multiple threads will not do anything that disagrees with the class documentation and, will not do anything that disagrees with how a reaonable programmer thinks the class should behave in cases where the documentation is not absolutely clear.另一种看待它的方式:如果有人告诉您class是“线程安全的”,那么他们可能会告诉您,多个线程对类方法的并发调用不会做任何与 class 文档不一致的事情,并且,不要做任何不同意理性程序员认为 class 在文档不是绝对清楚的情况下应该表现的事情。

NOTE: That is a weaker definition of "thread safety" because it glosses over the fact that, using thread-safe components to build a system does not guarantee that the system itself will be thread-safe.注意:这是对“线程安全”的较弱定义,因为它掩盖了使用线程安全组件构建系统并不能保证系统本身是线程安全的事实。

Does everybody who uses your class clearly understand that no thread in the program may ever call reset() while any reference to the old singleton still exists?使用您的 class 的每个人是否都清楚地了解程序中的任何线程都不会调用reset()而对旧 singleton 的任何引用仍然存在? If so, then I would call that a weak design because it is very far from being "junior-programmer-safe," but I would grudgingly admit that, from a strict, language-lawyerly point of view, you could call your ObjectFactory class " thread safe."如果是这样,那么我会称其为弱设计,因为它远非“初级程序员安全”,但我不情愿地承认,从严格的语言法律角度来看,您可以将ObjectFactory称为 class “线程安全。”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM