帮助查看以下代码,它是否安全?

时间:2009-06-01 09:31:12

标签: java multithreading static-methods

private static Callback callback;

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

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

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

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

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

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

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

7 个答案:

答案 0 :(得分:6)

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

但请注意,为true,只有static关键字会产生非线程安全的代码。

答案 1 :(得分:5)

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

选项1:此处所有实例共享相同的回调

private static final Callback callback = new Callback();

public Foo() {
    super(callback);
}

选项2:此处每个实例都有自己的回调

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

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

答案 2 :(得分:3)

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

答案 3 :(得分:2)

我认为你自己总结得很完美,但如果没有更多关于你想要达到的目标的详细信息,提出解决问题的建议将会非常棘手。

一个显而易见的问题是,callback必须是静态的吗?或者你可以安全地使它成为一个实例字段而不破坏你的类的功能吗?

答案 4 :(得分:2)

我知道它已被回答,但其原因尚未详细说明。

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

  1. 线程1 - callback = new Callback();
  2. 线程2 - callback = new Callback();
  3. 主题1 - 返回actionCallback;
  4. 线程2 - 返回actionCallback;
  5. 在这种情况下,(2.)生成的回调将在(3.)和(4。)中返回

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

    我希望有所帮助。

答案 5 :(得分:1)

你要做的事情被称为单身人士模式,如果你进行搜索,你可以找出为什么通常一个好主意,如果你可以避免这种模式,但是如果你需要它,你可以做到以下几点。 / p>

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

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

答案 6 :(得分:1)

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

关于如何做出不同变体的一些草图 - 仅仅从头顶开始,不要太过于字面意思:)

请注意,我假设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;
  }

并且,对于completness,每个对象一次回调:

  private Callback callback;

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

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