`
RednaxelaFX
  • 浏览: 3015926 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

静态变量的初始化,你是否真的需要lazy?

阅读更多
老赵几天前提到double-check,昨天又提到属性的lazy加载,想起记这么一点:
在实现单例的时候,你是否真的需要用double-check之类的技巧来实现lazy创建实例?

使用单例模式但又想让它lazy创建实例,理由通常是“实例的创建可能伴随一些复杂的初始化计算,或者需要持有一些外部资源之类;如果程序中并没有使用到那个实例,那么付出的代码就白费了”。
但很多时候上述理由是个伪命题:不使用的类根本就不会被初始化。既然没有初始化,就不会付出代价去创建单例的实例。

Effective Java, 2nd推荐在Java 5或更新的版本中使用enum来实现Singleton模式。那看看这段代码的输出:
public class Program {
    public static void main(String[] args) {
        System.out.println("main()");
        System.out.println("leaving main()");
    }
}

enum Singleton {
    INSTANCE;
    Singleton() {
        System.out.println("Singleton()");
    }
}

结果是:
引用
main()
leaving main()

虽然定义了Singleton类型,但程序中并没有使用它,所以也就没有引发它的初始化。
那么在main()里添加一行:
public class Program {
    public static void main(String[] args) {
        System.out.println("main()");
        Singleton s = Singleton.INSTANCE;
        System.out.println("leaving main()");
    }
}

enum Singleton {
    INSTANCE;
    Singleton() {
        System.out.println("Singleton()");
    }
}

结果变为:
引用
main()
Singleton()
leaving main()

哪儿用哪儿才初始化。

只要实现Singleton的时候,该类上没有别的对外公开的静态变量或者静态方法,外界想要使用Singleton实例只能通过INSTANCE的话,那引发Singleton初始化的只有对INSTANCE的访问(这时初始化是正确的)或者通过反射(例如Class.forName(),这是例外情况)。
要是有信心代码里不会出现“例外情况”的话,又何必费神去实现double-check呢?

C#里则有些细微差异。某个类的静态初始化“想当然”也应该是初次使用的时候才进行,但实际上这取决于静态初始化逻辑是怎么写的。如果写成:
using System;

public class Singleton {
    public static Singleton Instance = new Singleton();
    private Singleton() {
        Console.WriteLine("Singleton()");
    }
}

static class Program {
    static void Main(string[] args) {
        Console.WriteLine("Main()");
        Console.WriteLine("leaving Main()");
    }

    static void Foo() {
        var s = Singleton.Instance;
    }
}

虽然代码中有Foo()使用了Singleton,但实际执行路径上没有经过Foo(),其它地方也没用过Singleton类,于是输出结果是:
引用
Main()
leaving Main()

但要是在Main()里添加一行:
using System;

public class Singleton {
    public static Singleton Instance = new Singleton();

    private Singleton() {
        Console.WriteLine("Singleton()");
    }
}

static class Program {
    static void Main(string[] args) {
        Console.WriteLine("Main()");
        var s = Singleton.Instance;
        Console.WriteLine("leaving Main()");
    }

    static void Foo() {
        var s = Singleton.Instance;
    }
}

输出结果跟想像的可能就不一样了:
引用
Singleton()
Main()
leaving Main()

这是因为C#中直接在静态变量声明的地方就初始化,而且没有显式提供静态构造器实现的话,会使类带上beforefieldinit标记,使得类的静态初始化提早执行。稍微改改,给Singleton类添加一个空的静态构造器的话……
using System;

public class Singleton {
    public static Singleton Instance = new Singleton();

    static Singleton() {
    }

    private Singleton() {
        Console.WriteLine("Singleton()");
    }
}

static class Program {
    static void Main(string[] args) {
        Console.WriteLine("Main()");
        var s = Singleton.Instance;
        Console.WriteLine("leaving Main()");
    }

    static void Foo() {
        var s = Singleton.Instance;
    }
}

会发现执行结果变为:
引用
Main()
Singleton()
leaving Main()

这种写法就不会使类带上beforefieldinit,于是初始化时间就跟“想像中”的一样,哪儿用到哪儿才初始化。把Instance的初始化整个挪到静态构造器里的结果也一样。

有时候费了力气去写个double-check搞不好还写错了,要是回头发现其实不用自己费神写lazy逻辑也能达到效果的话,那肯定郁闷坏了。引用老赵的帖的标题:如果是能简单解决的问题,就不用想得太复杂了

^ ^
分享到:
评论
4 楼 mercyblitz 2010-06-23  
这是ClassLoader机制加载的,记载类的时候就synchronized啦,因此不会出现重复加载或者不可见的情况。
3 楼 RednaxelaFX 2009-09-06  
Saito 写道
http://crazybob.org/2007/01/lazy-loading-singletons.html

    Guice 这边还有一种 singleton . 不过不知道在c#上是不是也适用.. 

嗯我知道这个办法。C#里应该也可以用吧,原理差不多。不过我没测试过,回头试试。
2 楼 Saito 2009-09-06  
   睡糊涂了..  url 点成 quote 了..
1 楼 Saito 2009-09-06  
引用
http://crazybob.org/2007/01/lazy-loading-singletons.html


    Guice 这边还有一种 singleton . 不过不知道在c#上是不是也适用.. 

相关推荐

    System.Lazy 实例的延迟初始化值

    System.Lazy<T> 实例的延迟初始化值

    lazy-static.rs:一个用于在Rust中定义惰性求值静态变量的小宏

    使用此宏,可能具有static ,这些static要求在运行时执行代码才能进行初始化。 这包括需要堆分配的任何内容,例如向量或哈希图,以及需要计算非const函数调用的所有内容。最小支持的rustc 1.27.2+ 此版本已在CI中...

    lazy-static.rs, 在 Rust 中,用于定义惰性计算的static 变量的小宏.zip

    lazy-static.rs, 在 Rust 中,用于定义惰性计算的static 变量的小宏 lazy-static.rs在 Rust 中声明延迟求值的静态的宏。使用这里宏,可以以使 static s 在运行时要求执行代码,以便初始化。 这包括需要堆分配,如...

    lazy-property:对象属性的延迟初始化

    向对象添加延迟初始化的属性。 例子 var addLazyProperty = require ( "lazy-property" ) var obj = { } addLazyProperty ( obj , "foo" , function ( ) { console . log ( "initialized!" ) return "bar" } ) //...

    Lazy

    Lazy

    java stream 初始化及懒调用源代码

    java stream的初始化及懒加载源代码, 文章《java 中Stream初始化及懒调用(Lazy Invocation)》 地址:https://blog.csdn.net/2301_77345366/article/details/130017616

    Swift-Lazy-Containers:Swift 5.1的一些延迟初始化容器类型

    在Swift 5.1中具有延迟初始化值的几种方法。 请注意,如果您对Swift的lazy关键字的行为感到满意,则应使用该关键字。 这是针对: :不可重置的惰性模式,以确保Swift语言版本之间的惰性行为 :可惰性模式,其值仅在...

    lazyload延迟加载

    Lazyload是通过延迟加载来实现按需加载,达到节省资源,加快浏览速度的目的。 网上也有不少类似的效果,这个Lazyload主要特点是: 支持使用window(窗口)或元素作为容器对象; 对静态(位置大小不变)元素做了大量...

    LazyProperty:延迟初始化对象属性的实用程序

    在许多情况下,必须对私有/受保护的属性进行延迟初始化,许多人编写的类如下所示: class SomeService { protected $ dependency ; public function doWork () { $ this -> getDependency ()-> delegateWork ()...

    16Lazy,函数,并行

    2-创建一个静态私有变量数据类型是当前类的数据类型且不进行初始化 3-提供一个公有静态的获取当前对象的方法 4-进行判断, 若当前对象没有被创建, 创建对象, 否则返回 object LazyDemo extends App { def init():...

    jQuery_lazyload

    jQuery_lazyload插件示例Demo

    Lazy_Theta_star

    Lazy_Theta_star是在 Theta_star上的进一步改进,Theta_star是当节点加入open表时和当前点的父节点进行比较g值是否更小,对一些不必要的节点计算浪费了时间,而Lazy_Theta_star则是在弹出open表后进行比较,减少了...

    lazyload-JavaScript

    lazyload, JavaScript, 图片懒加载, h5

    Lazyload应用案例

    图片懒加载lazyload,增快页面访问速度。

    Lazy Load Plugin for jQuery demo

    Lazy Load Plugin for jQuery

    C#使用Lazy延迟加载

    博客与文章作为例子,查询一个博客的时候并不是需要将其下面的文章都加载进来。把需要延迟加载的属性使用Lazy泛型,实例时候传入一个Fun委托。

    jQuery.lazyload.js

    Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预加载的处理方式正好是相反的. 在包含很多...

    lazyload技术内幕

    lazyload技术内幕,当下最流行的图片浏览技术

    js-lazy-object:JavaScript 库,用于在仅在访问时初始化的对象上定义惰性属性。 也称为延迟初始化和缓存记忆的 getter

    LazyObject.js 允许您定义对象的属性,该对象的值仅在第一次访问时初始化。 从那时起,值被缓存/记忆。 它的defineLazyProperty函数紧跟Object.defineProperty签名,允许您更改定义的属性是否可configurable 、 ...

    Lazy Nezumi Pro 17.12.15.2233 破解补丁+中文化

    Lazy Nezumi Pro 是一个 Windows 应用程序,可以帮助你画出流畅,优美的线条,用鼠标或手写板。 它能与许多你喜爱的程序协作。可以让你拥有独特的笔刷稳定系统。本工具支持绝大多数的 2D 和 3D 绘图软件。 在 ...

Global site tag (gtag.js) - Google Analytics