A5) Singleton(单态模式) 定义:保证一个类只存在一个实例,并提供一个全局访问的指针。 这个也是非常常见的模式,可以说很难在一个项目中不使用这个模式。对于全局变量来说,唯一和同步是至关重要的,一般来说以下面的这种方式来实现。 public class Singleton { private Singleton(){} private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } } 第一行必须用private的构造函数,这样才能防止别的class生成Singleton的实例,而第二行的static定义让变量instance在所有Singleton实例中是唯一的(当然,这个例子实际上只有一个实例),那么每次通过getInstance()得到的变量instance就是Singleton的唯一实例。还有一种方法,如下: public class Singleton { private Singleton(){} private static Singleton instance = null; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 这种方法叫做lazy initialization技术,不过关于这种技术的争议很多,牵涉到double-checked locking(DCL)问题。关于这个问题,稍微解释一下,具体我也不是太明确,有兴趣可以另外研究。编译器在编译执行命令的时候,由于会使用代码优化,在synchronized段中的代码就有可能会被提前执行以加快代码效率。这个时候,也就是instance = new Singleton();这部分语句可能被预先优化执行,那么它的唯一性也就不能保证了。虽然还只是在理论上推论这个结果,并没有用事实来验证这一说法,现在也没发现这个方法造成的不良后果,但是,我觉得还是推荐第一种方法,简单而容易理解。
注:感谢rwyx的指点,因为以前理解有偏差,所以将上面部分删除后重新改写。 这种方法叫做lazy initialization技术,使用了synchronized关键字,不过这种方法的系统开销比较大,所以还是推荐地一种方法,简单而且效率高,最好在定义时追加final关键字。 关于Singleton,在C++/C#等语言中会用到一种叫做double-checked的技术,目的是为了解决lazy initializtion技术中同步代码效率低的问题。具体实现如下: public static Singleton getInstance() { if (instance == null) { synchronized { if (instance == null) {instance = new Singleton();} } } return instance; } 即在同步前做一次判断,这样只有第一次时会进入同步段,这样效率也很高。不过这种方法在java中却是错误的。因为java语言规范和C++/C# 等不同,是一个非常灵活的规范。java编译器可以自由地重排变量的初始化和访问顺序,以提高运行时效率;同时java中的变量访问是可以被自动缓存到寄存器的,这也导致潜在的JIT 编译器相关的依赖性错误。每个编译器可能有不同的实现方法,同样的代码在不同编译器和执行环境下可能有不同的表现。解决的办法有很多,使用线程局部存储(ThreadLocal)来保存instance变量,或者使用volatile关键字来定义instance,强制java编译器不进行优化。DCL参考资料: http://www./User8/flier_lu/blog/1620823.html
参考: 1、 http://www./designpatterns/singleton.htm(中文、java实例) 2、 http://www./Patterns/PatternSingleton.aspx(英文、C#实例、UML) 3、 http://www./tech/DesignPattern/Singleton.html(日文、java实例、UML)推荐
|