我想在我的程序中为会话提供一个可重置对象实例,该实例是线程安全的,会话的一个示例可能是登录的用户会话。 / p>
我目前正在做这样的事情;
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;
}
}
}
我想延迟创建对象,能够重置它。上面的方法是线程安全的吗?如果没有,可以解决这个问题吗?
再一个示例是此处的作用域对象基于用户会话具有一些内部数据,因此应该是每个用户会话的新实例。
答案 0 :(得分:0)
以上方法是线程安全的吗?
不,不是。
假设我们有两个线程-A
和B
。
A
调用getInstance()
,通过instance==null
检查,然后上下文切换到B
,调用reset()
。 B
完成执行reset()
之后,A
再次获取上下文并返回instance
,该值现在为空。
如果没有通用的解决方案?
我不记得用reset
方法看到单例,所以我不知道有任何 common 模式用于此问题。但是,最简单的解决方案是只删除if (instance == null)
中的第一个getInstance()
支票。这将使您的实现线程安全,因为instance
始终在同步块中进行检查和修改。在这种情况下,您也可以从volatile
中删除instance
修饰符,因为它总是从同步块中访问的。
我可以想到更复杂的解决方案,但是只有在实际分析表明您在该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
值。这种情况是否可以接受,取决于您。
答案 1 :(得分:0)
以上方法是线程安全的吗?
答案取决于您认为“线程安全”的含义。您的reset()
方法中没有任何东西可以防止先前称为getInstance()
的线程继续使用旧实例。
那是“线程安全的”吗?
通常来说,“线程安全”是指一个线程的操作永远不会导致其他线程看到处于不一致或无效状态的共享数据。但是,“不一致”或“无效”的含义取决于共享数据的结构(即,取决于应用程序的设计)。
另一种查看方式:如果有人告诉您 class 是“线程安全的”,那么他们可能是在告诉您多个线程并发调用类的方法不会起作用任何与类文档不一致的内容,以及与合理的程序员在类文档不是十分清楚的情况下认为类应该的行为都不同的事情。
注意:这是对“线程安全”的较弱定义,因为它掩盖了以下事实:使用线程安全组件来构建系统并不能保证系统本身将是线程安全的。
每个清楚地使用您的类的人 都明白,在对旧单例的任何引用仍然存在的情况下,程序中没有线程可以调用reset()
吗?如果是这样,那么我会称其为弱设计,因为它远非“初级程序员安全”,但我会勉强承认,从严格的语言法律角度来看,您可以称呼{{ 1}}类“ 线程安全。”