java中的单例

我只是读了下面的代码:

public class SingletonObjectDemo {

    private static SingletonObjectDemo singletonObject;
    // Note that the constructor is private
    private SingletonObjectDemo() {
        // Optional Code
    }
    public static SingletonObjectDemo getSingletonObject() {
        if (singletonObject == null) {
            singletonObject = new SingletonObjectDemo();
       }
       return singletonObject;
    }
}

我需要知道这部分需要什么:

if (singletonObject == null) {
    singletonObject = new SingletonObjectDemo();
}

如果我们不使用这部分代码呢? 仍然会有SingletonObjectDemo的单个副本,为什么我们需要这个代码呢?


这个类有一个字段SingletonObjectDemo singletonObject持有单例实例。 现在有两种可能的策略 -

1 - 您用声明进行对象的热切初始化 -

private static SingletonObjectDemo singletonObject = new SingletonObjectDemo();

这会导致你的单例对象在类加载的时候被初始化。 这种策略的缺点是,如果你有很多单身人士,即使他们不需要,他们也会被初始化并占用内存。

2 - 对对象进行延迟初始化,即在第一次调用getSingletonObject()时初始化它 -

// note that this initializes the object to null by default
private static SingletonObjectDemo singletonObject;

...

if (singletonObject == null) {
        singletonObject = new SingletonObjectDemo();
    }

这为您节省了内存,直到真正需要单例。 这种策略的缺点是,第一次调用方法可能会看到稍差的响应时间,因为它在返回之前必须初始化obejct。


懒惰与渴望的初始化

if语句是延迟初始化技术的实现。

更明确的版本如下:

private boolean firstTime = true;
private Stuff stuff;

public Stuff gimmeStuff() {
   if (firstTime) {
      firstTime = false;
      stuff = new Stuff();
   }
   return stuff;
}

会发生什么是第一次调用gimmeStuff()firstTime将是true ,所以stuff将被初始化为new Stuff() 。 在随后的调用中, firstTime将为false ,所以不会再调用new Stuff()

因此, stuff被“懒惰地”初始化。 它在第一次需要时才会被初始化。

也可以看看

  • 维基百科/懒惰初始化
  • Wikipedia / Initialization on demand holder idiom

  • 在线程安全

    需要说的是,片段不是线程安全的。 如果有多个线程,那么在某些竞争条件下,可能会多次调用new SingletonObjectDemo()

    一种解决方案是synchronized getSingletonObject()方法。 但是,这对所有对getSingletonObject()调用getSingletonObject()产生同步开销。 然后使用所谓的双重检查锁定语法来尝试修正这一问题,但在Java中,直到J2SE 5.0在新的内存模型中引入volatile关键字时,这种习语才真正起作用。

    不用说,单身模式的正确执行并不是一件微不足道的事情。

    也可以看看

  • developerWorks / Java:双重检查锁定和Singleton模式 - 综合了解这种破坏性编程习惯用法
  • 维基百科/双重检查锁定
  • 相关问题

  • 同步块vs同步方法?

  • 有效的Java第二版

    以下是这本书在这些主题上要说的话:

    项目71:明智地使用延迟初始化

    与大多数优化的情况一样,懒惰初始化的最佳建议是“除非需要,否则不要这样做”。 懒惰初始化是一把双刃剑。 它降低了初始化类或创建实例的成本,代价是增加了访问延迟初始化字段的成本。 根据延迟初始化字段的一小部分最终需要初始化,初始化它们的花费是多少,以及每个字段被访问的频率如何,延迟初始化(像许多“优化”实际上会损害性能)。

    在多线程的情况下,延迟初始化非常棘手。 如果两个或两个以上的线程共享一个懒惰的初始化字段,那么使用某种形式的同步至关重要,否则会导致严重的错误。

    在大多数情况下,正常初始化优于延迟初始化。

    项目3:使用私有构造函数或enum类型强制使用单例属性

    截至1.5版本。 还有第三种方法来实施单身人士。 只需使用一个元素创建一个枚举类型。 [...]这种方法在功能上等同于public字段方法,只是它更简洁,提供了免费的序列化机制,并提供了针对多个实例的铁证保证,即使面对复杂的序列化或基于反射的攻击。

    [...]单元枚举类型是实现单例的最好方法。

    相关问题

    enum单例/ Java实现上:

  • 在Java中实现单例模式的有效方法
  • Java Enum Singleton
  • 比较Java枚举成员:==或equals()?
  • 单线程中的线程安全
  • 单身模式的优点和选择:

  • 关于设计模式:何时使用Singleton?
  • 单身人士在编程中的目的
  • 单身人士有什么不好
  • 单身:好的设计还是拐杖?
  • 什么是Singleton的替代品
  • 辛格尔顿:应该如何使用它

  • 如果我们不使用这部分代码呢? 仍然会有SingletonObjectDemo的单个副本,为什么我们需要这个代码呢?

    这个想法是延迟加载单例,即只在实际需要时加载实例。 你为什么想这么做? 那么Bob Lee在Lazy Loading Singletons中总结得非常好:

    在生产中,您通常希望急切地加载所有单例,以便尽早发现错误并提前展现性能,但在测试和开发期间,您只需加载绝对需要的内容,以免浪费时间。

    但是你显示的实现被破坏,它不是线程安全的,并且两个并发线程实际上可以创建两个实例。 使您的懒加载的单例线程安全的最好方法是使用初始化按需持有者(IODH)成语,它非常简单并且没有同步开销。 引用有效Java,项目71:明智地使用惰性初始化(重点不是我的):

    如果您需要在静态字段上使用延迟初始化来实现性能,请使用延迟初始化持有者类成语 。 这个习惯用法(也称为按需初始化持有者类成语)利用了保证一个类在被使用之前不会被初始化[JLS,12.4.1]。 以下是它的外观:

    // Lazy initialization holder class idiom for static fields
    private static class FieldHolder {
        static final FieldType field = computeFieldValue();
    }
    static FieldType getField() { return FieldHolder.field; }
    

    当第一次调用getField方法时,它首次读取FieldHolder.field ,导致FieldHolder类被初始化。 这个习惯用法的getField在于getField方法不同步并且只执行字段访问,所以懒惰的初始化几乎不会增加访问的成本。 一个现代的虚拟机将同步字段访问只用于初始化类。 一旦类被初始化,虚拟机将修补代码,以便后续访问该字段不涉及任何测试或同步。

    也可以看看

  • 项目66:同步对共享可变数据的访问 - Effective Java 2nd edition
  • 第71项:明智地使用惰性初始化 - Effective Java第2版
  • 新内存模型是否修复了“双重检查锁定”问题?
  • 链接地址: http://www.djcxy.com/p/91839.html

    上一篇: Singleton in java

    下一篇: Why Java does not allow overriding equals(Object) in an Enum?