为什么饥饿的单身模式是线程安全的

时间:2018-03-27 04:50:14

标签: java design-patterns thread-safety singleton

这是单例设计模式实现之一:饥饿的实现。

我已经了解到这种方式只能创建一个实例并保持线程安全。

很容易理解,只有一个实例,因为实例只在类加载过程中创建。

但是为什么这可以是线程安全的呢?在线文档说它也是因为实例是在加载类时创建的。但我不明白这一点,这个实例如何在多线程情况下保持同步?这对我来说很模糊。有人可以回答这个问题,提前谢谢你。

  public class Hunger {
        private static Hunger instance = new Hunger();

        private Hunger() {}    

        public static Hunger getInstance() {
            return instance; //When multi threads call this, there maybe a problem since only instance is provided.
        }
    }

3 个答案:

答案 0 :(得分:1)

为了确保你安全地初始化你的单例,你可以使用一种特殊的类,称为枚举。例如:

public class GameActivity extends Activity implements SensorEventListener {

  //...
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // ...
    sensorManager.registerListener((SensorEventListener) this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
  }

  // This is implemented method from SensorEventListener
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // do something
  }

  // This is implemented method from SensorEventListener
  public void onSensorChanged(SensorEvent event) {
    // do something
  }
}

你会得到这样的输出:

enum Hungry{
    INSTANCE;
    private static Random rd;

    static {
        rd = new Random();
        System.out.println("Initializing object...");
    }
    public Integer nextNum() {
        return new Random().nextInt(10);
    }

}

public class LazyEval {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Start...");

        Thread.sleep(1000);
        System.out.println("First call");
        System.out.println(Hungry.INSTANCE.nextNum());
        Thread.sleep(1000);
        System.out.println("Second Call");
        System.out.println(Hungry.INSTANCE.nextNum());

    }
}

这是因为,当您第一次访问INSTANCE时,您的JVM将初始化静态块上的所有字段。通过这种方式,您可以存档实时延迟初始化并避免竞争条件。

答案 1 :(得分:0)

Singleton 的线程问题是确保您只初始化一次,并始终向每个提出要求的人提供相同的对象。如果您将初始化推迟到第一个呼叫:

public static Hunger getInstance() {
    if (instance == null) {
        instance = new Hunger();
    }
    return instance;
}

你最终可以在if块中同时使用两个线程,两次调用new,然后会出现错误。

这是一个如何同步Hunger对象本身的单独问题;那不是关于 Singleton 模式。

答案 2 :(得分:0)

延迟初始化会导致线程同步问题。 (Lazy init表示在调用createInstance()或getInstance()时创建单例实例)。 但是静态Hunger实例的初始化时间比线程创建时间快。 它不是懒惰的初始化。

但它不是完全同步。 因为它只在创建实例时可用。 也许在线文档说这只是在创建singlton实例时可用。 因此,在放置任何公共资源成员时必须使用线程锁定对象。