3
private static Callback callback;

public Foo()
{
    super(getCallback());
}

private static Callback getCallback()
{
    callback = new Callback();
    return callback;
}

构造函数 Foo() 可能会从多个线程中调用。我关心的是私有静态字段“callback”和静态方法“getCallback()”。

可以看出,每次调用“getCallback()”时,都会为静态字段“回调”分配一个新值。

我的猜测是它不是线程安全的,因为关键字static总是附加到类而不是实例上,这意味着 Foo 的静态字段“回调”可能会被构造另一个 Foo() 的其他线程覆盖. 它是否正确?

如果我错了,请纠正我。谢谢!

编辑:我的目的是在课堂上的某个地方保留“回调”,以便以后可以重用它。但这并不容易,因为 Foo 从一个类扩展而来,该类具有强制传递“回调”的构造函数。

4

7 回答 7

6

是的,你是对的。当两个线程同时进入方法并且一个为静态字段分配一个新的而另一个已经完成但尚未返回时,两个实例可能以Foo相同的实例结束。在这种情况下,最好的解决方法是不要使用静态字段,因为它没有任何用处。或者,使同步。CallBackgetCallback()CallBackgetCallback()

但请注意,只有关键字导致代码不是线程安全的,这是不正确的。static

于 2009-06-01T09:41:57.150 回答
5

它不是线程安全的。尝试以下替代方案:

选项 1:这里所有实例共享相同的回调

private static final Callback callback = new Callback();

public Foo() {
    super(callback);
}

选项2:这里每个实例都有自己的回调

public Foo() {
    super(new Callback());
}

注意,在这两种情况下,虽然构造函数是线程安全的,但是整个类的线程安全取决于Callback的实现。如果它具有可变状态,那么您将遇到潜在问题。如果 Callback 是不可变的,那么您就具有线程安全性。

于 2009-06-01T09:52:12.387 回答
3

每次调用 Foo() 时(即使来自同一个线程),回调都会获得一个新值。我不太确定您的代码应该做什么(如果您只想初始化静态变量一次(单例),您应该检查它是否在 getCallback() 中仍然为空 - 以及 actionCallback 是什么?)。为了使其线程安全,请使用同步。

于 2009-06-01T09:43:03.327 回答
2

我认为您自己完美地总结了它,但是如果没有更多关于您要实现的目标的详细信息,提出解决问题的建议将很棘手。

一个明显的问题是,callback必须是静态的吗?或者你能安全地将它变成一个实例字段而不破坏你的类的功能吗?

于 2009-06-01T09:44:01.200 回答
2

我知道它已经得到了回答,但原因并没有真正详细说明。

它有两个线程正在调用 getCallback() 方法,它们可以执行如下行:

  1. 线程 1 - 回调 = new Callback();
  2. 线程 2 - 回调 = new Callback();
  3. 线程 1 - 返回 actionCallback;
  4. 线程 2 - 返回 actionCallback;

在这种情况下,在 (2.) 处生成的回调将在 (3.) 和 (4.) 中返回

解决方案似乎是询问如果回调特定于实例而不是类,为什么静态定义回调。

我希望这会有所帮助。

于 2009-06-01T16:58:15.017 回答
1

您正在尝试做的事情称为单例模式,如果您进行搜索,您会发现为什么如果可以的话避免这种模式通常是一个好主意,但是如果您需要它,您可以执行以下操作。

private static final Callback CALLBACK= new Callback();

或者,如果您需要一个懒惰的单身人士,您可以这样做

public class Foo {
   class CallbackHolder {
       static final Callback CALLBACK= new Callback();
   }

   public static Callback getCallback() {
      return CallbackHolder.CALLBACK;
   }

public Foo() {
    super(getCallback());
}

两种实现都是线程安全的。

于 2009-06-02T06:22:57.377 回答
1

你想要每个线程一个回调,每个对象一个回调,还是一个真正的单例?

一些关于如何做不同变体的草图 - 只是从我的脑海中,不要太从字面上理解:)

请注意,我假设 Callback 有一个非平凡的构造函数,它可能会抛出需要处理的异常,如果它是一个平凡的构造函数,您可以大大简化所有这些。

每个线程一个:

  private static ThreadLocal<Callback> callback;

  public Foo()
  {
      super(getCallback());
  }

  private static Callback getCallback()
  {
      if ( callback.get() == null ) 
          callback.set(new Callback());
      return callback.get();
  }

所有线程的单一回调:

  private final static Callback callback;

  static {
      callback = new Callback(); 
  }

  public Foo()
  {
      super(getCallback());
  }

  private static Callback getCallback()
  {
      return callback;
  }

而且,为了完整起见,每个对象一个回调:

  private Callback callback;

  public Foo()
  {
      super(getCallback());
  }

  private Callback getCallback()
  {
      callback = new Callback();
      return callback;
  }
于 2009-06-02T07:42:02.823 回答