0

当需要一个单例时,静态字段是一个优雅的解决方案吗?

class HelperSingleton {
  static Helper singleton = new Helper();

  public static Helper getInstance() {
       return singleton;
     }
  }

当两个线程同时访问getInstance时,该字段singleton是否有可能没有完全初始化?或者查看辅助对象字段的默认值,而不是构造函数中设置的值?静态单例也是懒初始化?

我的意思是, static Helper singleton = new Helper(); 这个分配是原子的吗?并且不会返回默认值?

4

4 回答 4

1

1)当一个线程访问静态getInstance时,第一次类加载器必须加载HelperSingleton类,它会在加载类之前锁定其他线程。因此存在隐式同步。J.Bloch "Effective Java" Item 71现代 VM 将同步字段访问,仅用于初始化类。一旦类被初始化,VM 将修补代码,以便后续对该字段的访问不涉及任何测试或同步。

2)你的单身是懒惰的,因为只有一个访问点——getInstance。不仅实例是按需创建的,而且整个类都是按需加载的。一个类在使用之前不会被初始化 [JLS, 12.4.1]。

于 2013-05-15T15:43:22.353 回答
1

我认为单身人士最优雅的解决方案是:

public enum Singleton {

    INSTANCE;

    public Singleton getInstance() {
        return INSTANCE;
    }
}

使用单例模式有些问题,因为有时你不知道你真的只有其中一个。例如,考虑使用类加载器时的情况。

顺便说一下,这个问题(和其他问题)有一个彻底的解释。

于 2013-05-15T15:33:17.357 回答
0

看看http://en.wikipedia.org/wiki/Singleton_pattern

有多种样式可以解释好/坏。

于 2013-05-15T15:32:24.020 回答
0

如果是这样:

class HelperSingleton {
  static Helper singleton = null;
  public static Helper getInstance() {
      if(singleton == null) {
         singleton = new Helper();
      }
      return singleton;
    }
}

这里可能有:

  1. 线程 1 调用该getInstance()方法并确定它singletonnull.

  2. 线程 1 进入 if 块,但在创建新实例之前被线程 2 抢占。

  3. 线程 2 调用该getInstance()方法并确定该实例是null.

  4. 线程 2 进入 if 块并创建一个新Helper对象并将变量分配singleton给这个新对象。

  5. 线程 2 返回 Singleton 对象引用。

  6. 线程 2 被线程 1 抢占。

  7. 线程 1 从它停止的地方开始并执行singleton = new Helper();,这会导致创建另一个 Singleton 对象。

  8. 线程 1 返回此对象。

所以我们最终得到了两个实例。创建单身人士的最佳方式是使用 enum 回答here

static Helper singleton = new Helper(); 

Helper在这里,当加载类并singleton保存对该实例的引用时,会创建一个新的实例。您可以在JLS 12.4.2中获得详细的初始化过程。

于 2013-05-15T15:33:48.257 回答