Java模式设计之单例模式(四) - 编程入门网
下面没有使用任何线程安全考虑的错误例子。
从单线程的程序谈起 首先考虑一个单线程的版本。 代码清单13:没有使用任何线程安全措施的一个例子
这是一个错误的例子,详情请见下面的说明。 写出这样的代码,本意显然是要保持在整个JVM 中只有一个Helper 的实例;因此,才会有if (helper == null) 的检查。非常明显的是,如果在多线程的环境中运行,上面的代码会有两个甚至两个以上的Helper 对象被创建出来,从而造成错误。 但是,想像一下在多线程环境中的情形就会发现,如果有两个线程A 和B 几乎同时到达if (helper == null)语句的外面的话,假设线程A 比线程B 早一点点,那么: (1)A 会首先进入if (helper == null) 块的内部,并开始执行new Helper() 语句。此时,helper 变量仍然是null,直到线程A 的new Helper() 语句返回并给helper 变量赋值为止。 (2) 但是,线程B 并不会在if (helper == null)语句的外面等待,因为此时helper == null 是成立的,它会马上进入if (helper == null)语句块的内部。这样,线程B 会不可避免地执行helper = new Helper();语句,从而创建出第二个实例来。 (3)线程A 的helper = new Helper();语句执行完毕后,helper 变量得到了真实的对象引用,(helper == null)不再为真。第三个线程不会再进入if (helper == null) 语句块的内部了。 (4)线程B 的helper = new Helper(); 语句也执行完毕后,helper 变量的值被覆盖。但是第一个Helper 对象被线程A 引用的事实不会改变。 这时,线程A 和B 各自拥有一个独立的Helper 对象,而这是错误的。 Java模式设计之单例模式(四)(5)时间:2010-12-14线程安全的版本 为了克服没有线程安全的缺点,下面给出一个线程安全的例子。 代码清单14:这是一个正确的答案
显然,由于整个静态工厂方法都是同步化的,因此,不会有两个线程同时进入这个方法。因此,当线程A 和B 作为第一批调用者同时或几乎同时调用此方法时: (1)早到一点的线程A 会率先进入此方法,同时线程B 会在方法外部等待。 (2) 对线程A 来说,helper 变量的值是null ,因此helper = new Helper(); 语句会被执行。 (3)线程A 结束对方法的执行,helper 变量的值不再是null。 (4)线程B 进入此方法,helper 变量的值不再是null ,因此helper = new Helper(); 语句不会被执行。线程B 取到的是helper 变量所含有的引用,也就是对线程A 所创立的Helper 实例的引用。 显然,线程A 和B 持有同一个Helper 实例,这是正确的。 画蛇添足的“双重检查” 但是,仔细审察上面的正确答案会发现,同步化实际上只在helper 变量第一次被赋值之前才有用。在helper 变量有了值以后,同步化实际上变成了一个不必要的瓶颈。如果能有一个方法去掉这个小小的额外开销,不是更加完美了吗?因此,就有了下面这个设计“巧妙”的双重检查成例。在读者向下继续读之前,有必要提醒一句:正如本小节的标题所标明的那样,这是一个反面教材,因为双重检查成例在Java 编译器里无法实现。 代码清单15:使用双重检查成例的懒汉式单例模式
|
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |