??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲最大的成网4438,亚洲精品成人片在线观看,中文字幕亚洲免费无线观看日本http://www.tkk7.com/lsbwahaha/category/38811.htmlzh-cnMon, 09 May 2011 00:20:46 GMTMon, 09 May 2011 00:20:46 GMT60【{】单例模式:不可使用双重查锁?/title><link>http://www.tkk7.com/lsbwahaha/archive/2011/05/08/349788.html</link><dc:creator>胡鹏</dc:creator><author>胡鹏</author><pubDate>Sun, 08 May 2011 11:17:00 GMT</pubDate><guid>http://www.tkk7.com/lsbwahaha/archive/2011/05/08/349788.html</guid><wfw:comment>http://www.tkk7.com/lsbwahaha/comments/349788.html</wfw:comment><comments>http://www.tkk7.com/lsbwahaha/archive/2011/05/08/349788.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/lsbwahaha/comments/commentRss/349788.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/lsbwahaha/services/trackbacks/349788.html</trackback:ping><description><![CDATA[<p>单例创徏模式是一个通用的编E习语。和多线E一起用时Q必需使用某种cd的同步。在努力创徏更有效的代码ӞJava E序员们创徏了双重检查锁定习语,其和单例创建模式一起用,从而限制同步代码量。然而,׃一些不太常见的 Java 内存模型l节的原因,q不能保证这个双重检查锁定习语有效。它偶尔会失败,而不是d败。此外,它失败的原因q不明显Q还包含 Java 内存模型的一些隐U细节。这些事实将D代码p|Q原因是双重查锁定难于跟t。在本文余下的部分里Q我们将详细介绍双重查锁定习语,从而理解它在何处失效?/p> <p><a name="1"><span id="1hn97rn" class="atitle">单例创徏习语</span></a></p> <p>要理解双重检查锁定习语是从哪里v源的Q就必须理解通用单例创徏习语Q如清单 1 中的阐释Q?/p> <br /> <a name="code1"><strong>清单 1. 单例创徏习语</strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="65%" sizcache="2" sizset="40"> <tbody sizcache="1" sizset="40"> <tr> <td class="code-outline"> <pre class="displaycode"> import java.util.*; class Singleton { private static Singleton instance; private Vector v; private boolean inUse; private Singleton() { v = new Vector(); v.addElement(new Object()); inUse = true; } public static Singleton getInstance() { if (instance == null) //1 instance = new Singleton(); //2 return instance; //3 } } </pre> </td> </tr> </tbody> </table> <br /> <p>此类的设计确保只创徏一?<code>Singleton</code> 对象。构造函数被声明?<code>private</code>Q?code>getInstance()</code> Ҏ只创Z个对象。这个实现适合于单U程E序。然而,当引入多U程Ӟ必通过同步来保?<code>getInstance()</code> Ҏ。如果不保护 <code>getInstance()</code> ҎQ则可能q回 <code>Singleton</code> 对象的两个不同的实例。假设两个线Eƈ发调?<code>getInstance()</code> Ҏq且按以下顺序执行调用: </p> <ol> <li>U程 1 调用 <code>getInstance()</code> Ҏq决?<code>instance</code> ?//1 处ؓ <code>null</code>?<br /> <br /> </li> <li>U程 1 q入 <code>if</code> 代码块,但在执行 //2 处的代码行时被线E?2 预占?<br /> <br /> </li> <li>U程 2 调用 <code>getInstance()</code> Ҏq在 //1 处决?<code>instance</code> ?<code>null</code>?<br /> <br /> </li> <li>U程 2 q入 <code>if</code> 代码块ƈ创徏一个新?<code>Singleton</code> 对象q在 //2 处将变量 <code>instance</code> 分配l这个新对象?<br /> <br /> </li> <li>U程 2 ?//3 处返?<code>Singleton</code> 对象引用?br /> <br /> </li> <li>U程 2 被线E?1 预占?<br /> <br /> </li> <li>U程 1 在它停止的地方启动,q执?//2 代码行,q导致创建另一?<code>Singleton</code> 对象?<br /> <br /> </li> <li>U程 1 ?//3 处返回这个对象?</li> </ol> <p>l果?<code>getInstance()</code> Ҏ创徏了两?<code>Singleton</code> 对象Q而它本该只创Z个对象。通过同步 <code>getInstance()</code> Ҏ从而在同一旉只允怸个线E执行代码,q个问题得以ҎQ如清单 2 所C: </p> <br /> <a name="code2"><strong>清单 2. U程安全?getInstance() Ҏ</strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="41"> <tbody sizcache="1" sizset="41"> <tr> <td class="code-outline"> <pre class="displaycode"> public static synchronized Singleton getInstance() { if (instance == null) //1 instance = new Singleton(); //2 return instance; //3 } </pre> </td> </tr> </tbody> </table> <br /> <p>清单 2 中的代码针对多线E访?<code>getInstance()</code> Ҏq行得很好。然而,当分析这D代码时Q您会意识到只有在第一ơ调用方法时才需要同步。由于只有第一ơ调用执行了 //2 处的代码Q而只有此行代码需要同步,因此无需对后l调用用同步。所有其他调用用于决?<code>instance</code> 是非 <code>null</code> 的,q将其返回。多U程能够安全q发地执行除W一ơ调用外的所有调用。尽如此,׃该方法是 <code>synchronized</code> 的,需要ؓ该方法的每一ơ调用付出同步的代hQ即使只有第一ơ调用需要同步?</p> <p>Z此方法更为有效,一个被UCؓ双重查锁定的习语应q而生了。这个想法是Z避免寚wW一ơ调用外的所有调用都实行同步的昂贵代仗同步的代h在不同的 JVM 间是不同的。在早期Q代L当高。随着更高U的 JVM 的出玎ͼ同步的代价降低了Q但出入 <code>synchronized</code> Ҏ或块仍然有性能损失。不考虑 JVM 技术的q步Q程序员们绝不想不必要地费处理旉?/p> <p>因ؓ只有清单 2 中的 //2 行需要同步,我们可以只将其包装到一个同步块中,如清?3 所C: </p> <br /> <a name="code3"><strong>清单 3. getInstance() Ҏ</strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="42"> <tbody sizcache="1" sizset="42"> <tr> <td class="code-outline"> <pre class="displaycode"> public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { instance = new Singleton(); } } return instance; } </pre> </td> </tr> </tbody> </table> <br /> <p>清单 3 中的代码展示了用多线E加以说明的和清?1 相同的问题。当 <code>instance</code> ?<code>null</code> Ӟ两个U程可以q发地进?<code>if</code> 语句内部。然后,一个线E进?<code>synchronized</code> 块来初始?<code>instance</code>Q而另一个线E则被阻断。当W一个线E退?<code>synchronized</code> 块时Q等待着的线E进入ƈ创徏另一?<code>Singleton</code> 对象。注意:当第二个U程q入 <code>synchronized</code> 块时Q它q没有检?<code>instance</code> 是否?<code>null</code>?</p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="43"> <tbody sizcache="1" sizset="43"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="44"> <tbody sizcache="2" sizset="45"> <tr align="right" sizcache="2" sizset="45"> <td sizcache="2" sizset="45"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="45"> <tbody sizcache="1" sizset="45"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="2"><span id="bh7ldfh" class="atitle">双重查锁?/span></a></p> <p>为处理清?3 中的问题Q我们需要对 <code>instance</code> q行W二ơ检查。这是“双重查锁?#8221;名称的由来。将双重查锁定习语应用到清单 3 的结果就是清?4 ?</p> <br /> <a name="code4"><strong>清单 4. 双重查锁定示?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="46"> <tbody sizcache="1" sizset="46"> <tr> <td class="code-outline"> <pre class="displaycode"> public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 if (instance == null) //2 instance = new Singleton(); //3 } } return instance; } </pre> </td> </tr> </tbody> </table> <br /> <p>双重查锁定背后的理论是:?//2 处的W二ơ检查Q如清单 3 中那P创徏两个不同?<code>Singleton</code> 对象成ؓ不可能。假设有下列事g序列Q?</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?<br /> <br /> </li> <li>׃ <code>instance</code> ?<code>null</code>Q线E?1 ?//1 处进?<code>synchronized</code> 块?<br /> <br /> </li> <li>U程 1 被线E?2 预占?br /> <br /> </li> <li>U程 2 q入 <code>getInstance()</code> Ҏ?br /> <br /> </li> <li>׃ <code>instance</code> 仍旧?<code>null</code>Q线E?2 试图获取 //1 处的锁。然而,׃U程 1 持有该锁Q线E?2 ?//1 处阻塞?br /> <br /> </li> <li>U程 2 被线E?1 预占?br /> <br /> </li> <li>U程 1 执行Q由于在 //2 处实例仍旧ؓ <code>null</code>Q线E?1 q创Z?<code>Singleton</code> 对象q将其引用赋值给 <code>instance</code>?br /> <br /> </li> <li>U程 1 退?<code>synchronized</code> 块ƈ?<code>getInstance()</code> Ҏq回实例?<br /> <br /> </li> <li>U程 1 被线E?2 预占?br /> <br /> </li> <li>U程 2 获取 //1 处的锁ƈ?<code>instance</code> 是否?<code>null</code>?<br /> <br /> </li> <li>׃ <code>instance</code> 是非 <code>null</code> 的,q没有创建第二个 <code>Singleton</code> 对象Q由U程 1 创徏的对象被q回?</li> </ol> <p>双重查锁定背后的理论是完的。不q地是,现实完全不同。双重检查锁定的问题是:q不能保证它会在单处理器或多处理器计机上顺利运行?/p> <p>双重查锁定失败的问题q不归咎?JVM 中的实现 bugQ而是归咎?Java q_内存模型。内存模型允许所谓的“无序写入”Q这也是q些习语p|的一个主要原因?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="47"> <tbody sizcache="1" sizset="47"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="48"> <tbody sizcache="2" sizset="49"> <tr align="right" sizcache="2" sizset="49"> <td sizcache="2" sizset="49"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="49"> <tbody sizcache="1" sizset="49"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="3"><span id="nz7tjjt" class="atitle">无序写入</span></a></p> <p>释该问题Q需要重新考察上述清单 4 中的 //3 行。此行代码创Z一?<code>Singleton</code> 对象q初始化变量 <code>instance</code> 来引用此对象。这行代码的问题是:?<code>Singleton</code> 构造函C执行之前Q变?<code>instance</code> 可能成ؓ?<code>null</code> 的?/p> <p>什么?q一说法可能让您始料未及Q但事实实如此。在解释q个现象如何发生前,请先暂时接受q一事实Q我们先来考察一下双重检查锁定是如何被破坏的。假设清?4 中代码执行以下事件序列:</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?br /> <br /> </li> <li>׃ <code>instance</code> ?<code>null</code>Q线E?1 ?//1 处进?<code>synchronized</code> 块?<br /> <br /> </li> <li>U程 1 前进?//3 处,但在构造函数执?em>之前</em>Q实例成ؓ?<code>null</code>?<br /> <br /> </li> <li>U程 1 被线E?2 预占?br /> <br /> </li> <li>U程 2 查实例是否ؓ <code>null</code>。因为实例不?nullQ线E?2 ?<code>instance</code> 引用q回l一个构造完整但部分初始化了?<code>Singleton</code> 对象?<br /> <br /> </li> <li>U程 2 被线E?1 预占?br /> <br /> </li> <li>U程 1 通过q行 <code>Singleton</code> 对象的构造函数ƈ引用返回给它,来完成对该对象的初始化?</li> </ol> <p>此事件序列发生在U程 2 q回一个尚未执行构造函数的对象的时候?/p> <p>为展C此事g的发生情况,假设Z码行 <code>instance =new Singleton();</code> 执行了下列伪代码Q?<code>instance =new Singleton();</code> </p> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="50"> <tbody sizcache="1" sizset="50"> <tr> <td class="code-outline"> <pre class="displaycode">mem = allocate(); //Allocate memory for Singleton object. instance = mem; //Note that instance is now non-null, but //has not been initialized. ctorSingleton(instance); //Invoke constructor for Singleton passing //instance. </pre> </td> </tr> </tbody> </table> <br /> <p>q段伪代码不仅是可能的,而且是一?JIT ~译器上真实发生的。执行的序是颠倒的Q但鉴于当前的内存模型,q也是允许发生的。JIT ~译器的q一行ؓ使双重检查锁定的问题只不q是一ơ学术实践而已?/p> <p>明这一情况Q假设有清单 5 中的代码。它包含一个剥ȝ?<code>getInstance()</code> Ҏ。我已经删除?#8220;双重查?#8221;以简化我们对生成的汇~代码(清单 6Q的回顾。我们只兛_ JIT ~译器如何编?<code>instance=new Singleton();</code> 代码。此外,我提供了一个简单的构造函数来明确说明汇编代码中该构造函数的q行情况?</p> <br /> <a name="code5"><strong>清单 5. 用于演示无序写入的单例类</strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="51"> <tbody sizcache="1" sizset="51"> <tr> <td class="code-outline"> <pre class="displaycode"> class Singleton { private static Singleton instance; private boolean inUse; private int val; private Singleton() { inUse = true; val = 5; } public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } } </pre> </td> </tr> </tbody> </table> <br /> <p>清单 6 包含?Sun JDK 1.2.1 JIT ~译器ؓ清单 5 中的 <code>getInstance()</code> Ҏ体生成的汇编代码?</p> <br /> <a name="code6"><strong>清单 6. 由清?5 中的代码生成的汇~代?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="52"> <tbody sizcache="1" sizset="52"> <tr> <td class="code-outline"> <pre class="displaycode"> ;asm code generated for getInstance 054D20B0 mov eax,[049388C8] ;load instance ref 054D20B5 test eax,eax ;test for null 054D20B7 jne 054D20D7 054D20B9 mov eax,14C0988h 054D20BE call 503EF8F0 ;allocate memory 054D20C3 mov [049388C8],eax ;store pointer in ;instance ref. instance ;non-null and ctor ;has not run 054D20C8 mov ecx,dword ptr [eax] 054D20CA mov dword ptr [ecx],1 ;inline ctor - inUse=true; 054D20D0 mov dword ptr [ecx+4],5 ;inline ctor - val=5; 054D20D7 mov ebx,dword ptr ds:[49388C8h] 054D20DD jmp 054D20B0 </pre> </td> </tr> </tbody> </table> <br /> <p><strong>?</strong> 为引用下列说明中的汇~代码行Q我引用指令地址的最后两个|因ؓ它们都以 <code>054D20</code> 开头。例如,<code>B5</code> 代表 <code>test eax,eax</code>?/p> <p>汇编代码是通过q行一个在无限循环中调?<code>getInstance()</code> Ҏ的测试程序来生成的。程序运行时Q请q行 Microsoft Visual C++ 调试器ƈ其附到表示试E序?Java q程中。然后,中断执行q找到表C无限循环的汇~代码?/p> <p><code>B0</code> ?<code>B5</code> 处的前两行汇~代码将 <code>instance</code> 引用从内存位|?<code>049388C8</code> 加蝲?<code>eax</code> 中,q进?<code>null</code> 查。这跟清?5 中的 <code>getInstance()</code> Ҏ的第一行代码相对应。第一ơ调用此ҎӞ<code>instance</code> ?<code>null</code>Q代码执行到 <code>B9</code>?code>BE</code> 处的代码?<code>Singleton</code> 对象从堆中分配内存,q将一个指向该块内存的指针存储?<code>eax</code> 中。下一行代码,<code>C3</code>Q获?<code>eax</code> 中的指针q将其存储回内存位置?<code>049388C8</code> 的实例引用。结果是Q?code>instance</code> 现在为非 <code>null</code> q引用一个有效的 <code>Singleton</code> 对象。然而,此对象的构造函数尚未运行,q恰是破坏双重检查锁定的情况。然后,?<code>C8</code> 行处Q?code>instance</code> 指针被解除引用ƈ存储?<code>ecx</code>?code>CA</code> ?<code>D0</code> 行表C内联的构造函敎ͼ该构造函数将?<code>true</code> ?<code>5</code> 存储?<code>Singleton</code> 对象。如果此代码在执?<code>C3</code> 行后且在完成该构造函数前被另一个线E中断,则双重检查锁定就会失败?/p> <p>不是所有的 JIT ~译器都生成如上代码。一些生成了代码Q从而只在构造函数执行后?<code>instance</code> 成ؓ?<code>null</code>。针?Java 技术的 IBM SDK 1.3 版和 Sun JDK 1.3 都生成这L代码。然而,qƈ不意味着应该在这些实例中使用双重查锁定。该习语p|q有一些其他原因。此外,您ƈ不总能知道代码会在哪些 JVM 上运行,?JIT ~译器L会发生变化,从而生成破坏此习语的代码?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="53"> <tbody sizcache="1" sizset="53"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="54"> <tbody sizcache="2" sizset="55"> <tr align="right" sizcache="2" sizset="55"> <td sizcache="2" sizset="55"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="55"> <tbody sizcache="1" sizset="55"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="4"><span id="j7l9xzf" class="atitle">双重查锁定:获取两个</span></a></p> <p>考虑到当前的双重查锁定不起作用,我加入了另一个版本的代码Q如清单 7 所C,从而防止您刚才看到的无序写入问题?</p> <br /> <a name="code7"><strong>清单 7. 解决无序写入问题的尝?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="56"> <tbody sizcache="1" sizset="56"> <tr> <td class="code-outline"> <pre class="displaycode"> public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 inst = new Singleton(); //4 } instance = inst; //5 } } } return instance; } </pre> </td> </tr> </tbody> </table> <br /> <p>看着清单 7 中的代码Q您应该意识C情变得有点荒谬。请CQ创建双重检查锁定是Z避免对简单的三行 <code>getInstance()</code> Ҏ实现同步。清?7 中的代码变得难于控制。另外,该代码没有解决问题。仔l检查可h原因?/p> <p>此代码试N免无序写入问题。它试图通过引入局部变?<code>inst</code> 和第二个 <code>synchronized</code> 块来解决q一问题。该理论实现如下Q?</p> <ol> <li>U程 1 q入 <code>getInstance()</code> Ҏ?br /> <br /> </li> <li>׃ <code>instance</code> ?<code>null</code>Q线E?1 ?//1 处进入第一?<code>synchronized</code> 块?<br /> <br /> </li> <li>局部变?<code>inst</code> 获取 <code>instance</code> 的|该值在 //2 处ؓ <code>null</code>?<br /> <br /> </li> <li>׃ <code>inst</code> ?<code>null</code>Q线E?1 ?//3 处进入第二个 <code>synchronized</code> 块?<br /> <br /> </li> <li>U程 1 然后开始执?//4 处的代码Q同时 <code>inst</code> 为非 <code>null</code>Q但?<code>Singleton</code> 的构造函数执行前。(q就是我们刚才看到的无序写入问题。) <br /> <br /> </li> <li>U程 1 被线E?2 预占?br /> <br /> </li> <li>U程 2 q入 <code>getInstance()</code> Ҏ?br /> <br /> </li> <li>׃ <code>instance</code> ?<code>null</code>Q线E?2 试图?//1 处进入第一?<code>synchronized</code> 块。由于线E?1 目前持有此锁Q线E?2 被阻断?br /> <br /> </li> <li>U程 1 然后完成 //4 处的执行?br /> <br /> </li> <li>U程 1 然后一个构造完整的 <code>Singleton</code> 对象?//5 处赋值给变量 <code>instance</code>Qƈ退两个 <code>synchronized</code> 块?<br /> <br /> </li> <li>U程 1 q回 <code>instance</code>?br /> <br /> </li> <li>然后执行U程 2 q在 //2 处将 <code>instance</code> 赋值给 <code>inst</code>?br /> <br /> </li> <li>U程 2 发现 <code>instance</code> 为非 <code>null</code>Q将其返回?</li> </ol> <p>q里的关键行?//5。此行应该确?<code>instance</code> 只ؓ <code>null</code> 或引用一个构造完整的 <code>Singleton</code> 对象。该问题发生在理论和实际彼此背道而驰的情况下?/p> <p>׃当前内存模型的定义,清单 7 中的代码无效。Java 语言规范QJava Language SpecificationQJLSQ要求不能将 <code>synchronized</code> 块中的代码移出来。但是,q没有说不能?<code>synchronized</code> 块外面的代码U?em>?/em> <code>synchronized</code> 块中?</p> <p>JIT ~译器会在这里看C个优化的Z。此优化会删?//4 ?//5 处的代码Q组合ƈ且生成清?8 中所C的代码?</p> <br /> <a name="code8"><strong>清单 8. 从清?7 中优化来的代码?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="57"> <tbody sizcache="1" sizset="57"> <tr> <td class="code-outline"> <pre class="displaycode"> public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { //1 Singleton inst = instance; //2 if (inst == null) { synchronized(Singleton.class) { //3 //inst = new Singleton(); //4 instance = new Singleton(); } //instance = inst; //5 } } } return instance; } </pre> </td> </tr> </tbody> </table> <br /> <p>如果q行此项优化Q您同样遇到我们之前讨的无序写入问题?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="58"> <tbody sizcache="1" sizset="58"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="59"> <tbody sizcache="2" sizset="60"> <tr align="right" sizcache="2" sizset="60"> <td sizcache="2" sizset="60"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="60"> <tbody sizcache="1" sizset="60"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="5"><span id="vbrd79v" class="atitle">?volatile 声明每一个变量怎么P</span></a></p> <p>另一个想法是针对变量 <code>inst</code> 以及 <code>instance</code> 使用关键?<code>volatile</code>。根?JLSQ参?<a >参考资?/a>Q,声明?<code>volatile</code> 的变量被认ؓ是顺序一致的Q即Q不是重新排序的。但是试图?<code>volatile</code> 来修正双重检查锁定的问题Q会产生以下两个问题Q?/p> <ul> <li>q里的问题不是有关顺序一致性的Q而是代码被移动了Q不是重新排序?br /> <br /> </li> <li>即考虑了顺序一致性,大多数的 JVM 也没有正地实现 <code>volatile</code>?</li> </ul> <p>W二点值得展开讨论。假设有清单 9 中的代码Q?/p> <br /> <a name="code9"><strong>清单 9. 使用?volatile 的顺序一致?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="61"> <tbody sizcache="1" sizset="61"> <tr> <td class="code-outline"> <pre class="displaycode"> class test { private volatile boolean stop = false; private volatile int num = 0; public void foo() { num = 100; //This can happen second stop = true; //This can happen first //... } public void bar() { if (stop) num += num; //num can == 0! } //... } </pre> </td> </tr> </tbody> </table> <br /> <p>Ҏ JLSQ由?<code>stop</code> ?<code>num</code> 被声明ؓ <code>volatile</code>Q它们应该顺序一致。这意味着如果 <code>stop</code> 曄?<code>true</code>Q?code>num</code> 一定曾被设|成 <code>100</code>。尽如此,因ؓ许多 JVM 没有实现 <code>volatile</code> 的顺序一致性功能,您就不能依赖此行为。因此,如果U程 1 调用 <code>foo</code> q且U程 2 q发地调?<code>bar</code>Q则U程 1 可能?<code>num</code> 被设|成?<code>100</code> 之前?<code>stop</code> 讄?<code>true</code>。这导致线E见?<code>stop</code> ?<code>true</code>Q?<code>num</code> 仍被讄?<code>0</code>。?<code>volatile</code> ?64 位变量的原子数还有另外一些问题,但这已超Z本文的讨围。有x主题的更多信息,请参?<a >参考资?/a>?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="62"> <tbody sizcache="1" sizset="62"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="63"> <tbody sizcache="2" sizset="64"> <tr align="right" sizcache="2" sizset="64"> <td sizcache="2" sizset="64"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="64"> <tbody sizcache="1" sizset="64"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="6"><span id="xpfblld" class="atitle">解决Ҏ</span></a></p> <p>底线是Q无Z何种形式Q都不应使用双重查锁定,因ؓ您不能保证它在Q?JVM 实现上都能顺利运行。JSR-133 是有兛_存模型寻址问题的,管如此Q新的内存模型也不会支持双重查锁定。因此,您有两种选择Q?/p> <ul> <li>接受如清?2 中所C的 <code>getInstance()</code> Ҏ的同步?br /> <br /> </li> <li>攑ּ同步Q而用一?<code>static</code> 字段?</li> </ul> <p>选择?2 如清?10 中所C?/p> <br /> <a name="code10"><strong>清单 10. 使用 static 字段的单例实?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="65"> <tbody sizcache="1" sizset="65"> <tr> <td class="code-outline"> <pre class="displaycode"> class Singleton { private Vector v; private boolean inUse; private static Singleton instance = new Singleton(); private Singleton() { v = new Vector(); inUse = true; //... } public static Singleton getInstance() { return instance; } } </pre> </td> </tr> </tbody> </table> <br /> <p>清单 10 的代码没有用同步,q且保调用 <code>static getInstance()</code> Ҏ时才创徏 <code>Singleton</code>。如果您的目标是消除同步Q则q将是一个很好的选择?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="66"> <tbody sizcache="1" sizset="66"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="67"> <tbody sizcache="2" sizset="68"> <tr align="right" sizcache="2" sizset="68"> <td sizcache="2" sizset="68"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="68"> <tbody sizcache="1" sizset="68"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="7"><span id="d79x999" class="atitle">String 不是不变?/span></a></p> <p>鉴于无序写入和引用在构造函数执行前变成?<code>null</code> 的问题,您可能会考虑 <code>String</code> cR假设有下列代码Q?/p> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="69"> <tbody sizcache="1" sizset="69"> <tr> <td class="code-outline"> <pre class="displaycode">private String str; //... str = new String("hello"); </pre> </td> </tr> </tbody> </table> <br /> <p><code>String</code> cd该是不变的。尽如此,鉴于我们之前讨论的无序写入问题,那会在这里导致问题吗Q答案是肯定的。考虑两个U程讉K <code>String str</code>。一个线E能看见 <code>str</code> 引用一?<code>String</code> 对象Q在该对象中构造函数尚未运行。事实上Q清?11 包含展示q种情况发生的代码。注意,q个代码仅在我测试用的旧?JVM 上会p|。IBM 1.3 ?Sun 1.3 JVM 都会如期生成不变?<code>String</code>?/p> <br /> <a name="code11"><strong>清单 11. 可变 String 的例?/strong></a><br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="70"> <tbody sizcache="1" sizset="70"> <tr> <td class="code-outline"> <pre class="displaycode"> class StringCreator extends Thread { MutableString ms; public StringCreator(MutableString muts) { ms = muts; } public void run() { while(true) ms.str = new String("hello"); //1 } } class StringReader extends Thread { MutableString ms; public StringReader(MutableString muts) { ms = muts; } public void run() { while(true) { if (!(ms.str.equals("hello"))) //2 { System.out.println("String is not immutable!"); break; } } } } class MutableString { public String str; //3 public static void main(String args[]) { MutableString ms = new MutableString(); //4 new StringCreator(ms).start(); //5 new StringReader(ms).start(); //6 } } </pre> </td> </tr> </tbody> </table> <br /> <p>此代码在 //4 处创Z?<code>MutableString</code> c,它包含了一?<code>String</code> 引用Q此引用?//3 处的两个U程׃n。在?//5 ?//6 处,在两个分开的线E上创徏了两个对?<code>StringCreator</code> ?<code>StringReader</code>。传入一?<code>MutableString</code> 对象的引用?code>StringCreator</code> c进入到一个无限@环中q且使用?#8220;hello”?//1 处创?<code>String</code> 对象?code>StringReader</code> 也进入到一个无限@环中Qƈ且在 //2 处检查当前的 <code>String</code> 对象的值是不是 “hello”。如果不行,<code>StringReader</code> U程打印Z条消息ƈ停止。如?<code>String</code> cL不变的,则从此程序应当看不到M输出。如果发生了无序写入问题Q则?<code>StringReader</code> 看到 <code>str</code> 引用的惟一Ҏl不是gؓ“hello”?<code>String</code> 对象?/p> <p>在旧版的 JVM ?Sun JDK 1.2.1 上运行此代码会导致无序写入问题。ƈ因此D一个非不变?<code>String</code>?/p> <br /> <table border="0" cellspacing="0" cellpadding="0" width="100%" sizcache="2" sizset="71"> <tbody sizcache="1" sizset="71"> <tr> <td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br /> <img border="0" alt="" src="http://www.ibm.com/i/c.gif" width="8" height="6" /></td> </tr> </tbody> </table> <table class="no-print" cellspacing="0" cellpadding="0" align="right" sizcache="2" sizset="72"> <tbody sizcache="2" sizset="73"> <tr align="right" sizcache="2" sizset="73"> <td sizcache="2" sizset="73"><img alt="" src="http://www.ibm.com/i/c.gif" width="100%" height="4" /><br /> <table border="0" cellspacing="0" cellpadding="0" sizcache="2" sizset="73"> <tbody sizcache="1" sizset="73"> <tr> <td valign="middle"><img border="0" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br /> </td> <td valign="top" align="right"><strong></strong></td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <br /> <br /> <p><a name="7"><span id="vr97j97" class="atitle">l束?/span></a></p> <p>为避免单例中代h高昂的同步,E序员非常聪明地发明了双重检查锁定习语。不q的是,鉴于当前的内存模型的原因Q该习语未得到q泛使用Q就明显成ؓ了一U不安全的编E结构。重定义脆弱的内存模型这一领域的工作正在进行中。尽如此,即是在新提议的内存模型中,双重查锁定也是无效的。对此问题最佳的解决Ҏ是接受同步或者用一?<code>static field</code>?</p> <img src ="http://www.tkk7.com/lsbwahaha/aggbug/349788.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/lsbwahaha/" target="_blank">胡鹏</a> 2011-05-08 19:17 <a href="http://www.tkk7.com/lsbwahaha/archive/2011/05/08/349788.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java——Singleton模式http://www.tkk7.com/lsbwahaha/archive/2009/04/06/264078.html胡鹏胡鹏Mon, 06 Apr 2009 03:55:00 GMThttp://www.tkk7.com/lsbwahaha/archive/2009/04/06/264078.htmlhttp://www.tkk7.com/lsbwahaha/comments/264078.htmlhttp://www.tkk7.com/lsbwahaha/archive/2009/04/06/264078.html#Feedback0http://www.tkk7.com/lsbwahaha/comments/commentRss/264078.htmlhttp://www.tkk7.com/lsbwahaha/services/trackbacks/264078.html    

单态定?

Singleton模式主要作用是保证在Java应用E序中,一个类Class只有一个实例存在?/p>

在很多操作中Q比如徏立目?数据库连接都需要这L单线E操作?/p>

q有, singleton能够被状态化; q样Q多个单态类在一起就可以作ؓ一个状态仓库一样向外提供服务,比如Q你要论坛中的帖子计数器Q每ơ浏览一ơ需要计敎ͼ单态类能否保持住这个计敎ͼq且能synchronize的安全自动加1Q如果你要把q个数字怹保存到数据库Q你可以在不修改单态接口的情况下方便的做到?/p>

另外斚wQSingleton也能够被无状态化。提供工h质的功能,

Singleton模式׃ؓ我们提供了这样实现的可能。用Singleton的好处还在于可以节省内存Q因为它限制了实例的个数Q有利于Java垃圾回收Qgarbage collectionQ?/p>

我们常常看到工厂模式中类装入?class loader)中也用Singleton模式实现?因ؓ被装入的cd际也属于资源?/p>

如何使用?

一般Singleton模式通常有几UŞ?

public class Singleton {

  private Singleton(){}

  //在自己内部定义自׃个实例,是不是很奇怪?

  //注意q是private 只供内部调用

  private static Singleton instance = new Singleton();

  //q里提供了一个供外部讉K本class的静态方法,可以直接讉K  

  public static Singleton getInstance() {

    return instance;   

   }

}

W二UŞ?

public class Singleton {

  private static Singleton instance = null;

  public static synchronized Singleton getInstance() {

  //q个Ҏ比上面有所改进Q不用每ơ都q行生成对象Q只是第一ơ     

  //使用时生成实例,提高了效率!

  if (instance==null)

    instanceQnew Singleton();

  return instance;   }

}

使用Singleton.getInstance()可以讉K单态类?/p>

上面W二中Ş式是lazy initializationQ也是说第一ơ调用时初始SingletonQ以后就不用再生成了?/p>

注意到lazy initialization形式中的synchronizedQ这个synchronized很重要,如果没有synchronizedQ那么用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴者进一步研I?/p>

一般认为第一UŞ式要更加安全些?/p>

使用Singleton注意事项Q?/p>

有时在某些情况下Q用Singletonq不能达到Singleton的目的,如有多个Singleton对象同时被不同的c装入器装蝲Q在EJBq样的分布式pȝ中用也要注意这U情况,因ؓEJB是跨服务器,跨JVM的?/p>

我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocatorZE微分析一下:

在Pet Store中ServiceLocator有两U,一个是EJB目录下;一个是WEB目录下,我们查这两个ServiceLocator会发现内容差不多Q都是提供EJB的查询定位服务,可是Z么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式QServiceLocator属于资源定位Q理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用Q所以ServiceLocator才分成两U,一U面向WEB服务的,一U是面向EJB服务的?/p>

Singleton模式看v来简单,使用Ҏ也很方便Q但是真正用好,是非怸ҎQ需要对Java的类 U程内存{概忉|相当的了解?/p>

MQ如果你的应用基于容器,那么Singleton模式用或者不用,可以使用相关替代技术?/p>

胡鹏 2009-04-06 11:55 发表评论
]]>
վ֩ģ壺 ޳ۺӰԺԺ| ŷ޾Ʒ˾þԻӰƬ| 㽶aavۺ| һaɫƬþٸһHƬѷ | ҹƬ7777| ۺɫ7777վ777| ɫַѴȫ| ޹˾þ| ѻɫappվ| ޹ŷպƷһ | 91һ߹ۿ| ԭ1769þѲ| gvС߹ۿ| Ƶ߹ۿ| һĻþ| 92˾ƷƵ| ޳aƬ߹ۿ!!!| ˳ɵӰվ| GVGVͬ| 벻Ļ18| Ƶվѹۿ| ޾Ʒھþ| þùѾƷ| ձһ| ֻˬִַ̼| ˾ƷƵ| avƬ߹ۿ| һëƬƬѹۿ| ձһ| aɫëƬ| һëƬ߹ۿ| ¶ۺ| ޹ƷAVþۺӰԺ| freeƵ| ޶Ƶ˵ӰԺ| ѲƵ| ĻƵѹۿ| ޼Ůۺ99| ŷձ| ҹ߹ۿ| 99Ƶ߹ۿ|