单例模式及JMM的无序性

单例模式及JMM的无序性<wbr style="line-height:25px"><br style="line-height:25px"><span style="color:#003366; line-height:25px">众所周知单例模式有有</span><span style="color:#993300; line-height:25px">饿汉式</span><span style="color:#003366; line-height:25px">与</span><span style="color:#993300; line-height:25px">懒汉式</span><span style="color:#003366; line-height:25px">两种。当一个单例类的初始化开销很大,而希望当用户实际上需要的时候才去创建单例类,</span><br style="line-height:25px"><span style="color:#003366; line-height:25px">就会考虑使用懒汉式延迟初始化,来提高程序的启动速度。但懒汉式并不容易使用。</span><br style="line-height:25px"><span style="color:#000080; line-height:25px">在多线程的环境下,如果不同步getInstance()方法会出现线程安全的问题,如果同步整个方法,那么getInstance()就完全变成</span><wbr style="line-height:25px"><span style="color:#ff6600; line-height:25px">串行</span><wbr style="line-height:25px"><span style="color:#000080; line-height:25px">,</span><br style="line-height:25px"><span style="color:#003366; line-height:25px">串行效率会降低10倍甚至100倍。因此,有些聪明的程序员就把C中常用的DCL(double-checkedlocking,中文名双重检查加锁)搬到JAVA上来,</span><br style="line-height:25px"> 看上去很强大。如下:<br style="line-height:25px"> 双重检查加锁<br style="line-height:25px"> Java代码<br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">publicclass</span><span style="color:#3366ff; line-height:25px"></span><span style="color:#ff6600; line-height:25px">Singleton</span><span style="color:#3366ff; line-height:25px">{<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">privatestatic</span><span style="color:#3366ff; line-height:25px">Singletoninstance;<br style="line-height:25px"></span><span style="color:#808080; line-height:25px">//私有构造函数</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">privateSingleton(){</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#808080; line-height:25px">//双重检查加锁的代码</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">publicstatic</span><span style="color:#3366ff; line-height:25px">SingletongetInstance(){</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">if</span><span style="color:#3366ff; line-height:25px">(instance==null){</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#ff6600; line-height:25px">synchronized</span><span style="color:#3366ff; line-height:25px">(</span><span style="color:#99cc00; line-height:25px">Singleton.class</span><span style="color:#3366ff; line-height:25px">){</span><span style="color:#808080; line-height:25px">//1</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">if</span><span style="color:#3366ff; line-height:25px">(instance==null){</span><span style="color:#808080; line-height:25px">//2</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#0000ff; line-height:25px">instance=newSingleton();</span><span style="color:#808080; line-height:25px">//3</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">return</span><span style="color:#3366ff; line-height:25px">instance;</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"> 1.<span style="color:#99cc00; line-height:25px">线程1</span>进入getInstance()方法。<br style="line-height:25px"> 2.由于instance为null,<span style="color:#99cc00; line-height:25px">线程1</span>在//1处进入synchronized块。<br style="line-height:25px"> 3.<span style="color:#99cc00; line-height:25px">线程1</span>被<span style="color:#808000; line-height:25px">线程2</span>预占。<br style="line-height:25px"> 4.<span style="color:#808000; line-height:25px">线程2</span>进入getInstance()方法。<br style="line-height:25px"> 5.由于instance仍旧为null,<span style="color:#99cc00; line-height:25px">线程2</span>试图获取//1处的锁。然而,由于<span style="color:#99cc00; line-height:25px">线程1</span>持有该锁,<span style="color:#808000; line-height:25px">线程2</span>在//1处阻塞。<br style="line-height:25px"> 6.<span style="color:#808000; line-height:25px">线程2</span>被<span style="color:#99cc00; line-height:25px">线程1</span>预占。<br style="line-height:25px"> 7.<span style="color:#99cc00; line-height:25px">线程1</span>执行,由于在//2处实例仍旧为null,<span style="color:#808000; line-height:25px">线程1</span>还创建一个Singleton对象并将其引用赋值给instance。<br style="line-height:25px"> 8.<span style="color:#99cc00; line-height:25px">线程1</span>退出synchronized块并从getInstance()方法返回实例。<br style="line-height:25px"> 9.<span style="color:#99cc00; line-height:25px">线程1</span>被<span style="color:#808000; line-height:25px">线程2</span>预占。<br style="line-height:25px"> 10.<span style="color:#808000; line-height:25px">线程2</span>获取//1处的锁并检查instance是否为null。<br style="line-height:25px"> 11.<span style="line-height:25px"><span style="color:#000080; line-height:25px"><wbr style="line-height:25px">由于instance是非null的,并没有创建第二个Singleton对象,由线程1创建的一个尚未执行构造函数的对象被返回<wbr style="line-height:25px">。<br style="line-height:25px"><wbr style="line-height:25px">为展示此事件的发生情况</wbr></wbr></wbr></span></span><wbr style="line-height:25px">, <div style="line-height:25px">假设为代码行<span style="color:#0000ff; line-height:25px">instance=newSingleton();</span>执行了下列伪代码:<br style="line-height:25px"><span style="color:#0000ff; line-height:25px">mem=allocate();</span><span style="color:#808080; line-height:25px">//AllocatememoryforSingletonobject.</span><br style="line-height:25px"><span style="color:#0000ff; line-height:25px">instance=mem;</span><span style="color:#808080; line-height:25px">//Notethatinstanceisnownon-null,but</span><span style="color:#808080; line-height:25px">hasnotbeeninitialized.</span><br style="line-height:25px"><span style="color:#0000ff; line-height:25px">ctorSingleton(instance);</span><span style="color:#808080; line-height:25px">//InvokeconstructorforSingletonpassinginstance.</span><br style="line-height:25px"> 这段伪代码不仅是可能的,而且是一些JIT编译器上真实发生的。执行的顺序是颠倒的,<br style="line-height:25px"> 但鉴于当前的内存模型,这也是允许发生的。<br style="line-height:25px"> DCL背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:<span style="color:#000080; line-height:25px">并不能保证它会在单处理器或多处理器计算机上顺利运行。<br style="line-height:25px"> DCL失败的问题并不归咎于JVM中的实现bug,而是归咎于Java平台内存模型</span>。内存模型允许所谓的“无序写入”,<br style="line-height:25px"> 这也是这些习语失败的一个主要原因。<br style="line-height:25px"> 详情资料可以查看:<a target="_blank" rel="nofollow" href="http://www.ibm.com/developerworks/cn/java/j-dcl.html" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">http://www.ibm.com/developerworks/cn/java/j-dcl.html</a><wbr style="line-height:25px"><br style="line-height:25px"> 上面也说了,究其原因是Java平台内存模型(JMM)--<br style="line-height:25px"> 这里简单介绍一下Java的内存模型:<br style="line-height:25px"> 不像大多数其它语言,Java定义了它和潜在硬件的关系,通过一个能运行所有Java平台的正式内存模型,能够实现Java“写一次,<br style="line-height:25px"> 到处运行”的诺言。通过比较,其它语言像C和C++,缺乏一个正式的内存模型;在这些语言中,程序继承了运行该程序硬件平台的内存模型。<br style="line-height:25px"> 当运行在同步(单线程)环境中,一段程序与内存交互相当的简单,或者说至少它的表现如此。程序存贮条目到内存位置上,<br style="line-height:25px"> 并且在下一次这些内存位置被检测的时候期望它们仍然在那儿。<br style="line-height:25px"> 实际上,原理是完全不同的。但是通过编译器,Java虚拟机(JVM)维持着一个复杂的幻想,并且硬件把它掩饰起来。虽然,我们认为程序将像程序代码中说明的顺序连续执行,但是事情不是总是这样发生的。编译器,处理器和缓存可以自由的随意的应用我们的程序和数据,只要它们不会影响到计算的结果。例如,<span style="color:#000080; line-height:25px">编译器能用不同的顺序从明显的程序解释中生成指令,并且存贮变量在寄存器中,而不是内存中;处理器可以并行或者颠倒次序的执行指令</span>;<br style="line-height:25px"> 缓存可以改变顺序的把提交内容写入到主存中。<br style="line-height:25px"><span style="color:#000080; line-height:25px"><wbr style="line-height:25px">Java内存模型(JMM)所说的只要环境维持了</wbr></span><span style="color:#ff6600; line-height:25px">as-if-serial</span><span style="color:#000080; line-height:25px">语法,所有的各种各样的再排序和优化是可以接受的<wbr style="line-height:25px">。</wbr></span><br style="line-height:25px"> 也就是说,只要你完成了同样的结果与你在一个严格的连续环境中指令被执行的结果一样。<br style="line-height:25px"> 编译器,处理器和缓存为了达到高性能,需要重新安排程序运行的顺序。<br style="line-height:25px"> 近年来,我们看到了计算机的计算性能有了极大的提高。处理器时钟频率的提高对高性能有着充分的贡献,<br style="line-height:25px"> 并行能力(管道的形式和超标量体系结构执行单元,动态指令分配表和灵活的执行,精密的多级内存缓存)的提高也是主要的贡献者。当今,编写编译器的任务变得极其复杂,因为在这些复杂性中,编译器必须能保护程序员。<br style="line-height:25px"> 当编写单线程程序时,你不会看到这些多种多样指令或者内存重新排序运行的结果。然而,在多线程程序中,情况则完全不同——一个线程可以读另一个线程已经写了的内存位置。如果线程A用某一种顺序修改了一些变量,在缺乏同步时,线程B可能用同样的顺序也看不到它们,或者根本看不到它们。那可能是由于编译器把指令重新排序或临时在寄存器中存储了变量,后来把它写到内存以外;或者是由于处理器并行的或与编译器指定的不同的顺序执行指令;或者是由于指令在内存的不同区域,而且缓存用与它们写进时不一样的顺序更新了相应的主存位置。<br style="line-height:25px"> 无论何种环境,多线程程序先天的缺乏可预见性。除非你通过使用同步明确地保证线程有一个一致的内存视图。<br style="line-height:25px"><span style="color:#000080; line-height:25px"><wbr style="line-height:25px">阎宏在《Java与模式》也有精辟的总结:在Java编译器中,Singleton类的初始化与instance变量赋值的顺序不可预料。如果一个线程在没有同步化的条件下读取instance引用,并调用这个对象的方法的话,可能会发现对象的初始化过程尚未完成,从而造成崩溃<wbr style="line-height:25px">。</wbr></wbr></span><br style="line-height:25px"> 由于JMM天生的无序性,导致了在Singleton构造函数执行之前,变量instance可能成为非null!!<br style="line-height:25px"> 假设<span style="color:#99cc00; line-height:25px">线程1</span>执行到<br style="line-height:25px"> Java代码<br style="line-height:25px"> instance=newSingleton();<br style="line-height:25px"> 这一句,但<wbr style="line-height:25px"><span style="color:#000080; line-height:25px">又未完成初始化时,被线程抢夺掉时间片,这时</span><span style="color:#808000; line-height:25px">线程2</span><span style="color:#000080; line-height:25px">判断了instance非空并返回<wbr style="line-height:25px">一个instance对象</wbr></span>,但好可惜,这只是一个未完成初始化的半成品!这个半成品的成员很可能是失效的。<br style="line-height:25px"><span style="line-height:25px"><wbr style="line-height:25px">举一个比较贴近实际的例子</wbr></span><wbr style="line-height:25px"><br style="line-height:25px"> Java代码<br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">publicclass</span><span style="color:#3366ff; line-height:25px">Test4{<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">publicstaticvoid</span><span style="color:#3366ff; line-height:25px"></span><span style="color:#808000; line-height:25px">main</span><span style="color:#3366ff; line-height:25px">(String[]args){<br style="line-height:25px"> Xx=newX();<br style="line-height:25px"> }<br style="line-height:25px"> }<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">class</span><span style="color:#3366ff; line-height:25px">X{<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">static</span><span style="color:#3366ff; line-height:25px">Stringname="rjx";<br style="line-height:25px"><br style="line-height:25px"></span><span style="color:#993300; line-height:25px">static</span><span style="color:#3366ff; line-height:25px">{<br style="line-height:25px"> System.out.println("---------运行静态块的代码------------");<br style="line-height:25px"> name="Jam";<br style="line-height:25px"> }<br style="line-height:25px"><br style="line-height:25px"> {<br style="line-height:25px"> System.out.println("--------运行实例块/初始化块的代码--------");<br style="line-height:25px"> }<br style="line-height:25px"><br style="line-height:25px"></span><span style="color:#993300; line-height:25px">public</span><span style="color:#3366ff; line-height:25px">X(){<br style="line-height:25px"> System.out.println("--------运行构造函数的代码-------");<br style="line-height:25px"> System.out.println("静态变量name现在的值是"+name);<br style="line-height:25px"> }<br style="line-height:25px"> }</span><br style="line-height:25px"><span style="line-height:25px"><wbr style="line-height:25px">结果显示</wbr></span><wbr style="line-height:25px">:<br style="line-height:25px"><span style="color:#3366ff; line-height:25px">---------运行静态块的代码------------<br style="line-height:25px"> --------运行实例块/初始化块的代码--------<br style="line-height:25px"> --------运行构造函数的代码-------</span><br style="line-height:25px"> 静态变量name现在的值是Jam<br style="line-height:25px"> 这段代码的结果相信大部分朋友都很清楚,可以得知一般程序的运行程序是:静态代码块/静态变量--&gt;实例块/实例变量--&gt;构造函数。<br style="line-height:25px"> 但如果将代码稍微改变下:<br style="line-height:25px"> Java代码<br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">publicclass</span><span style="color:#3366ff; line-height:25px">Test4{<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">publicstaticvoid</span><span style="color:#ff6600; line-height:25px">main</span><span style="color:#3366ff; line-height:25px">(String[]args){<br style="line-height:25px"> Xx=newX();<br style="line-height:25px"> }<br style="line-height:25px"> }<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">class</span><span style="color:#3366ff; line-height:25px">X{<br style="line-height:25px"></span><span style="color:#808080; line-height:25px">//增加自己的静态实例变量</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">static</span><span style="color:#3366ff; line-height:25px">Xx=newX();</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">static</span><span style="color:#3366ff; line-height:25px">Stringname="rjx";</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">static</span><span style="color:#3366ff; line-height:25px">{</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">System.out.println("---------运行静态块的代码------------");</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">name="Jam";</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">{</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">System.out.println("--------运行实例块/初始化块的代码--------");</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">public</span><span style="color:#3366ff; line-height:25px">X(){</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">System.out.println("--------运行构造函数的代码-------");</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">System.out.println("静态变量name现在的值是"+name);</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="line-height:25px"><wbr style="line-height:25px">结果显示</wbr></span><wbr style="line-height:25px">:<br style="line-height:25px"><span style="color:#3366ff; line-height:25px">--------运行实例块/初始化块的代码--------<br style="line-height:25px"> --------运行构造函数的代码-------<br style="line-height:25px"> 静态变量name现在的值是null<br style="line-height:25px"> ---------运行静态块的代码------------<br style="line-height:25px"> --------运行实例块/初始化块的代码--------<br style="line-height:25px"> --------运行构造函数的代码-------<br style="line-height:25px"> 静态变量name现在的值是Jam</span><br style="line-height:25px"> 结果可能有些出乎你的意料。name中间的值为null,这就是之前所说的失效值。<br style="line-height:25px"><span style="color:#000080; line-height:25px">返回上面的双重检查加锁的单例程序,因为并非整个</span><span style="color:#0000ff; line-height:25px">getInstance()</span><span style="color:#000080; line-height:25px">的是同步的。<br style="line-height:25px"> 一个线程当执行</span><span style="color:#0000ff; line-height:25px">getInstance()</span><span style="color:#000080; line-height:25px">的时候,JVM分配内存,分配Singleton,调用构造函数。<wbr style="line-height:25px">在完成分配内存并且instance字段已经设置,但构造函数还没调用之时<wbr style="line-height:25px">,被另外一个线程抢夺了时间片,这时它进行</wbr></wbr></span><span style="color:#0000ff; line-height:25px">getInstance</span><span style="color:#000080; line-height:25px">并且进行非空判断,<br style="line-height:25px"> 它认为</span><span style="color:#0000ff; line-height:25px">instance</span><span style="color:#000080; line-height:25px">不为空,跳过</span><span style="color:#0000ff; line-height:25px">synchronized</span><span style="color:#000080; line-height:25px">快,返回一个部分构造的</span><span style="color:#0000ff; line-height:25px">instance</span><span style="color:#000080; line-height:25px">对象!不必说,这个结果既不是我们期望的,也不是我们想象的。</span><br style="line-height:25px"> 使用Volatile关键字能解决问题但不可取<br style="line-height:25px"> 《HeadFirst设计模式》中提倡在Java5的环境下用Volatile修饰instance变量,<br style="line-height:25px"> 试图使instance变量被认为是顺序一致,不是重新排序的。因为Java5更改了Volatile的语义,<br style="line-height:25px"> 使Volatile变量的读写与syncrhonized有相当的含义。这样做虽然能使DCL的功能正确,效率却和在synchronizedblock内部单一检查相当,<br style="line-height:25px"> 甚至更差一点(因为检查了两次)。在我看来,DCL已经完全不具有物理意义了。而且大多数的JVM也没有正确地实现Volatile。<br style="line-height:25px"><span style="line-height:25px"><wbr style="line-height:25px">解决方案<br style="line-height:25px"></wbr></span><wbr style="line-height:25px"><span style="color:#000080; line-height:25px">底线就是:无论以何种形式,都不应使用双重检查锁定,因为您不能保证它在任何JVM实现上都能顺利运行。<br style="line-height:25px"> JSR-133是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。因此,您有三种选择:</span><br style="line-height:25px"><span style="line-height:25px"><wbr style="line-height:25px">第一个方案:</wbr></span></wbr></wbr></wbr></wbr></wbr></wbr> </div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">接受整个getInstance()方法的同步。</span><wbr style="line-height:25px"><br style="line-height:25px"><span style="color:#993300; line-height:25px">publicstatic</span><span style="color:#ff00ff; line-height:25px">synchronized</span><span style="color:#3366ff; line-height:25px">SingletongetInstance()<br style="line-height:25px"> {<br style="line-height:25px"> if(instance==null)</span><span style="color:#808080; line-height:25px">//1</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">instance=newSingleton();</span><span style="color:#808080; line-height:25px">//2</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">returninstance;</span><span style="color:#808080; line-height:25px">//3</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></wbr> </div> <div style="line-height:25px"><span style="line-height:25px">第二方案:</span></div> <div style="line-height:25px">放弃同步,而使用一个static字段。<wbr style="line-height:25px"><br style="line-height:25px"><span style="color:#993300; line-height:25px">class</span><span style="color:#3366ff; line-height:25px">Singleton<br style="line-height:25px"> {<br style="line-height:25px"> privateVectorv;<br style="line-height:25px"> privatebooleaninUse;<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">privatestatic</span><span style="color:#3366ff; line-height:25px">Singletoninstance=newSingleton();<br style="line-height:25px"> privateSingleton()<br style="line-height:25px"> {<br style="line-height:25px"> v=newVector();<br style="line-height:25px"> inUse=true;<br style="line-height:25px"> //...<br style="line-height:25px"> }<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">publicstatic</span><span style="color:#3366ff; line-height:25px">SingletongetInstance()<br style="line-height:25px"> {<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">return</span><span style="color:#3366ff; line-height:25px">instance;<br style="line-height:25px"> }<br style="line-height:25px"> }</span><br style="line-height:25px"> 没有使用同步,并且确保调用staticgetInstance()方法时才创建Singleton。<br style="line-height:25px"> 如果您的目标是消除同步,则这将是一个很好的选择。<br style="line-height:25px"><span style="line-height:25px">第三个方案:</span></wbr> </div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">使用静态内部类</span><wbr style="line-height:25px"><br style="line-height:25px"><span style="color:#993300; line-height:25px">publicclass</span><span style="color:#3366ff; line-height:25px">Singleton{<br style="line-height:25px"></span><span style="color:#993300; line-height:25px">private</span><span style="color:#3366ff; line-height:25px">Singleton(){<br style="line-height:25px"><br style="line-height:25px"> }<br style="line-height:25px"></span><span style="color:#808080; line-height:25px">//静态内部类</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">staticclass</span><span style="color:#3366ff; line-height:25px">SingletonInner{</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">staticSingleton</span><span style="color:#3366ff; line-height:25px">instance=newSingleton();</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">publicstatic</span><span style="color:#3366ff; line-height:25px">SingletongetInstance(){</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px"></span><span style="color:#993300; line-height:25px">return</span><span style="color:#3366ff; line-height:25px">SingletonInner.instance;</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span><br style="line-height:25px"> 使用静态内部类!它是由一个叫BobLee的人写下来的(最初忘记了是哪两个人提出)。<br style="line-height:25px"> 在加载<span style="color:#0000ff; line-height:25px">singleton</span>时并不加载它的内部类<span style="color:#0000ff; line-height:25px">SingletonInner</span>,而在调用<span style="color:#0000ff; line-height:25px">getInstance()</span>时调用<span style="color:#0000ff; line-height:25px">SingletonInner</span>时才加载<span style="color:#0000ff; line-height:25px">SingletonInner</span>,<br style="line-height:25px"> 从而调用singleton的构造函数,实例化singleton,从而在不需要同步的情况下,达到延迟初始化的效果。<span style="color:#000080; line-height:25px"></span></wbr> </div> </wbr></wbr></wbr></wbr>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值