﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-java fans-随笔分类-设计模式</title><link>http://www.blogjava.net/jlin/category/53810.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 02 Dec 2018 07:59:06 GMT</lastBuildDate><pubDate>Sun, 02 Dec 2018 07:59:06 GMT</pubDate><ttl>60</ttl><item><title>在java中写出完美的单例模式（转）</title><link>http://www.blogjava.net/jlin/archive/2018/11/27/433525.html</link><dc:creator>fly</dc:creator><author>fly</author><pubDate>Tue, 27 Nov 2018 14:51:00 GMT</pubDate><guid>http://www.blogjava.net/jlin/archive/2018/11/27/433525.html</guid><wfw:comment>http://www.blogjava.net/jlin/comments/433525.html</wfw:comment><comments>http://www.blogjava.net/jlin/archive/2018/11/27/433525.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jlin/comments/commentRss/433525.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jlin/services/trackbacks/433525.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body" style="margin-bottom: 20px; word-break: break-word;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1. 前言</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">单例(Singleton)应该是开发者们最熟悉的设计模式了，并且好像也是最容易实现的&#8212;&#8212;基本上每个开发者都能够随手写出&#8212;&#8212;但是，真的是这样吗？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">作为一个Java开发者，也许你觉得自己对单例模式的了解已经足够多了。我并不想危言耸听说一定还有你不知道的&#8212;&#8212;毕竟我自己的了解也的确有限，但究竟你自己了解的程度到底怎样呢？往下看，我们一起来聊聊看~</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">2. 什么是单例？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">单例对象的类必须保证只有一个实例存在&#8212;&#8212;这是维基百科上对单例的定义，这也可以作为对意图实现单例模式的代码进行检验的标准。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">对单例的实现可以分为两大类&#8212;&#8212;懒汉式和饿汉式，他们的区别在于：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><ul style="list-style: none; margin: 0px 0px 10px 30px; padding-left: 0px; font-size: 12px;"><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">懒汉式：指全局的单例实例在第一次被使用时构建。</p></li><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">饿汉式：指全局的单例实例在类装载时构建。</p></li></ul><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">从它们的区别也能看出来，日常我们使用的较多的应该是懒汉式的单例，毕竟按需加载才能做到资源的最大化利用嘛~</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3. 懒汉式单例</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">先来看一下懒汉式单例的实现方式。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3.1 简单版本</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">看最简单的写法Version 1：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Version 1</span> <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Single1 {     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single1 instance;     </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single1 getInstance() {         </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {             instance </span>= <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Single1();         }         </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> instance;     } }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">或者再进一步，把构造器改为私有的，这样能够防止被外部的类调用。</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Version 1.1</span> <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Single1 {     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single1 instance;     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> Single1() {}     </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single1 getInstance() {         </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {             instance </span>= <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Single1();         }         </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> instance;     } }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">我仿佛记得当初学校的教科书就是这么教的？&#8212;&#8212; 每次获取instance之前先进行判断，如果instance为空就new一个出来，否则就直接返回已存在的instance。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这种写法在大多数的时候也是没问题的。问题在于，当多线程工作的时候，如果有多个线程同时运行到if (instance == null)，都判断为null，那么两个线程就各自会创建一个实例&#8212;&#8212;这样一来，就不是单例了。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3.2 synchronized版本</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">那既然可能会因为多线程导致问题，那么加上一个同步锁吧！</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">修改后的代码如下，相对于Version1.1，只是在方法签名上多加了一个synchronized：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Version 2 </span> <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Single2 {     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single2 instance;     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> Single2() {}     </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">synchronized</span><span style="line-height: 1.5 !important;"> Single2 getInstance() {         </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {             instance </span>= <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Single2();         }         </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> instance;     } }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">OK，加上synchronized关键字之后，getInstance方法就会锁上了。如果有两个线程（T1、T2）同时执行到这个方法时，会有其中一个线程T1获得同步锁，得以继续执行，而另一个线程T2则需要等待，当第T1执行完毕getInstance之后（完成了null判断、对象创建、获得返回值之后），T2线程才会执行执行。&#8212;&#8212;所以这端代码也就避免了Version1中，可能出现因为多线程导致多个实例的情况。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">但是，这种写法也有一个问题：给gitInstance方法加锁，虽然会避免了可能会出现的多个实例问题，但是会强制除T1之外的所有线程等待，实际上会对程序的执行效率造成负面影响。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3.3 双重检查（Double-Check）版本</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">Version2代码相对于Version1d代码的效率问题，其实是为了解决1%几率的问题，而使用了一个100%出现的防护盾。那有一个优化的思路，就是把100%出现的防护盾，也改为1%的几率出现，使之只出现在可能会导致多个实例出现的地方。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;有没有这样的方法呢？当然是有的，改进后的代码Vsersion3如下：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Version 3 </span> <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Single3 {     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single3 instance;     </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> Single3() {}     </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single3 getInstance() {         </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {             </span><span style="color: #0000ff; line-height: 1.5 !important;">synchronized</span> (Single3.<span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;">) {                 </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {                     instance </span>= <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Single3();                 }             }         }         </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> instance;     } }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这个版本的代码看起来有点复杂，注意其中有两次if (instance == null)的判断，这个叫做『双重检查 Double-Check』。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><ul style="list-style: none; margin: 0px 0px 10px 30px; padding-left: 0px; font-size: 12px;"><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">第一个if (instance == null)，其实是为了解决Version2中的效率问题，只有instance为null的时候，才进入synchronized的代码段&#8212;&#8212;大大减少了几率。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p></li><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">第二个if (instance == null)，则是跟Version2一样，是为了防止可能出现多个实例的情况。</p></li></ul><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212; 这段代码看起来已经完美无瑕了。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212; 当然，只是『看起来』，还是有小概率出现问题的。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这弄清楚为什么这里可能出现问题，首先，我们需要弄清楚几个概念：原子操作、指令重排。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">知识点：什么是原子操作？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">简单来说，原子操作（atomic）就是不可分割的操作，在计算机中，就是指不会因为线程调度被打断的操作。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">比如，简单的赋值是一个原子操作：</p><div style="margin: 5px 0px; font-size: 12px !important;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;">m = 6; <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 这是个原子操作</span></pre></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">假如m原先的值为0，那么对于这个操作，要么执行成功m变成了6，要么是没执行m还是0，而不会出现诸如m=3这种中间态&#8212;&#8212;即使是在并发的线程中。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">而，声明并赋值就不是一个原子操作：</p><div style="margin: 5px 0px; font-size: 12px !important;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #0000ff; line-height: 1.5 !important;">int</span> n = 6; <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 这不是一个原子操作</span></pre></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">对于这个语句，至少有两个操作：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#9312;声明一个变量n</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#9313;给n赋值为6</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;这样就会有一个中间状态：变量n已经被声明了但是还没有被赋值的状态。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;这样，在多线程中，由于线程执行顺序的不确定性，如果两个线程都使用m，就可能会导致不稳定的结果出现。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">知识点：什么是指令重排？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">简单来说，就是计算机为了提高执行效率，会做的一些优化，在不影响最终结果的情况下，可能会对一些语句的执行顺序进行调整。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">比如，这一段代码：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #0000ff; line-height: 1.5 !important;">int</span> a ;   <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 语句1 </span> <span style="line-height: 1.5 !important;"> a </span>= 8 ;   <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 语句2</span>  <span style="color: #0000ff; line-height: 1.5 !important;">int</span> b = 9 ;     <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 语句3</span>  <span style="color: #0000ff; line-height: 1.5 !important;">int</span> c = a + b ; <span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 语句4</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">正常来说，对于顺序结构，执行的顺序是自上到下，也即1234。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">但是，由于指令重排的原因，因为不影响最终的结果，所以，实际执行的顺序可能会变成3124或者1324。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">由于语句3和4没有原子性的问题，语句3和语句4也可能会拆分成原子操作，再重排。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;也就是说，对于非原子性的操作，在不影响最终结果的情况下，其拆分成的原子操作可能会被重新排列执行顺序。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">OK，了解了原子操作和指令重排的概念之后，我们再继续看Version3代码的问题。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">下面这段话直接从陈皓的文章(深入浅出单实例SINGLETON设计模式)中复制而来：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">主要在于singleton = new Singleton()这句，这并非是一个原子操作，事实上在 JVM 中这句话大概做了下面 3 件事情。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1. 给 singleton 分配内存</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">2. 调用 Singleton 的构造函数来初始化成员变量，形成实例</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3. 将singleton对象指向分配的内存空间（执行完这步 singleton才是非 null 了）</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的，最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者，则在 3 执行完毕、2 未执行之前，被线程二抢占了，这时 instance 已经是非 null 了（但却没有初始化），所以线程二会直接返回 instance，然后使用，然后顺理成章地报错。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">再稍微解释一下，就是说，由于有一个『instance已经不为null但是仍没有完成初始化』的中间状态，而这个时候，如果有其他线程刚好运行到第一层if (instance == null)这里，这里读取到的instance已经不为null了，所以就直接把这个中间状态的instance拿去用了，就会产生问题。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这里的关键在于&#8212;&#8212;线程T1对instance的写操作没有完成，线程T2就执行了读操作。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3.4 终极版本：volatile</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">对于Version3中可能出现的问题（当然这种概率已经非常小了，但毕竟还是有的嘛~），解决方案是：只需要给instance的声明加上volatile关键字即可，Version4版本：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Version 4 </span>  <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Single4 {      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">volatile</span><span style="line-height: 1.5 !important;"> Single4 instance;      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> Single4() {}      </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> Single4 getInstance() {          </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {              </span><span style="color: #0000ff; line-height: 1.5 !important;">synchronized</span> (Single4.<span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;">) {                  </span><span style="color: #0000ff; line-height: 1.5 !important;">if</span> (instance == <span style="color: #0000ff; line-height: 1.5 !important;">null</span><span style="line-height: 1.5 !important;">) {                      instance </span>= <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Single4();                  }              }          }          </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> instance;      }  }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">volatile关键字的一个作用是禁止指令重排，把instance声明为volatile之后，对它的写操作就会有一个内存屏障（什么是内存屏障？），这样，在它的赋值完成之前，就不用会调用读操作。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">注意：volatile阻止的不singleton = new Singleton()这句话内部[1-2-3]的指令重排，而是保证了在一个写操作（[1-2-3]）完成之前，不会调用读操作（if (instance == null)）。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;也就彻底防止了Version3中的问题发生。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;好了，现在彻底没什么问题了吧？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">好了，别紧张，的确没问题了。大名鼎鼎的EventBus中，其入口方法EventBus.getDefault()就是用这种方法来实现的。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">不过，非要挑点刺的话还是能挑出来的，就是这个写法有些复杂了，不够优雅、简洁。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">4. 饿汉式单例</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">下面再聊了解一下饿汉式的单例。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">如上所说，饿汉式单例是指：指全局的单例实例在类装载时构建的实现方式。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">由于类装载的过程是由类加载器（ClassLoader）来执行的，这个过程也是由JVM来保证同步的，所以这种方式先天就有一个优势&#8212;&#8212;能够免疫许多由多线程引起的问题。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">4.1 饿汉式单例的实现方式</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">饿汉式单例的实现如下：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;">饿汉式实现</span>  <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> SingleB {      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">final</span> SingleB INSTANCE = <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> SingleB();      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> SingleB() {}      </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span><span style="line-height: 1.5 !important;"> SingleB getInstance() {          </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> INSTANCE;      }  }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">对于一个饿汉式单例的写法来说，它基本上是完美的了。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">所以它的缺点也就只是饿汉式单例本身的缺点所在了&#8212;&#8212;由于INSTANCE的初始化是在类加载时进行的，而类的加载是由ClassLoader来做的，所以开发者本来对于它初始化的时机就很难去准确把握：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><ol style="padding-left: 40px;"><li style="list-style-type: decimal;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">可能由于初始化的太早，造成资源的浪费</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p></li><li style="list-style-type: decimal;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">如果初始化本身依赖于一些其他数据，那么也就很难保证其他数据会在它初始化之前准备好。</p></li></ol><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">当然，如果所需的单例占用的资源很少，并且也不依赖于其他数据，那么这种实现方式也是很好的。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">知识点：什么时候是类装载时？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">前面提到了单例在类装载时被实例化，那究竟什么时候才是『类装载时』呢？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">不严格的说，大致有这么几个条件会触发一个类被加载：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">1. new一个对象时</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">2. 使用反射创建它的实例时</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">3. 子类被加载时，如果父类还没被加载，就先加载父类</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">4. jvm启动时执行的主类会首先被加载</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">5. 一些其他的实现方式</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">5.1 Effective Java 1 &#8212;&#8212; 静态内部类</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">《Effective Java》一书的第一版中推荐了一个中写法：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Effective Java 第一版推荐写法</span>  <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> Singleton {      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">class</span><span style="line-height: 1.5 !important;"> SingletonHolder {          </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">final</span> Singleton INSTANCE = <span style="color: #0000ff; line-height: 1.5 !important;">new</span><span style="line-height: 1.5 !important;"> Singleton();      }      </span><span style="color: #0000ff; line-height: 1.5 !important;">private</span><span style="line-height: 1.5 !important;"> Singleton (){}      </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">static</span> <span style="color: #0000ff; line-height: 1.5 !important;">final</span><span style="line-height: 1.5 !important;"> Singleton getInstance() {          </span><span style="color: #0000ff; line-height: 1.5 !important;">return</span><span style="line-height: 1.5 !important;"> SingletonHolder.INSTANCE;      }  }</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这种写法非常巧妙：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><ul style="list-style: none; margin: 0px 0px 10px 30px; padding-left: 0px; font-size: 12px;"><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">对于内部类SingletonHolder，它是一个饿汉式的单例实现，在SingletonHolder初始化的时候会由ClassLoader来保证同步，使INSTANCE是一个真&#183;单例。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p></li><li style="list-style-type: disc;"><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">同时，由于SingletonHolder是一个内部类，只在外部类的Singleton的getInstance()中被使用，所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。</p></li></ul><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212;它利用了ClassLoader来保证了同步，同时又能让开发者控制类加载的时机。从内部看是一个饿汉式的单例，但是从外部看来，又的确是懒汉式的实现。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">简直是神乎其技。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">5.2 Effective Java 2 &#8212;&#8212; 枚举</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">你以为到这就算完了？不，并没有，因为厉害的大神又发现了其他的方法。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">《Effective Java》的作者在这本书的第二版又推荐了另外一种方法，来直接看代码：</p><div style="margin: 5px 0px; font-size: 12px !important;"><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word; font-family: &quot;Courier New&quot; !important;"><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> Effective Java 第二版推荐写法</span>  <span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">enum</span><span style="line-height: 1.5 !important;"> SingleInstance {      INSTANCE;      </span><span style="color: #0000ff; line-height: 1.5 !important;">public</span> <span style="color: #0000ff; line-height: 1.5 !important;">void</span><span style="line-height: 1.5 !important;"> fun1() {           </span><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> do something</span> <span style="line-height: 1.5 !important;">     }  }     </span><span style="color: #008000; line-height: 1.5 !important;">//</span><span style="color: #008000; line-height: 1.5 !important;"> 使用</span> <span style="line-height: 1.5 !important;"> SingleInstance.INSTANCE.fun1();</span></pre><div style="margin-top: 5px;"><span style="padding-right: 5px; line-height: 1.5 !important;"><a title="复制代码" style="color: #1a8bc8; border: none !important;"><img src="https://common.cnblogs.com/images/copycode.gif" alt="复制代码" style="max-width: 900px; height: auto; border: none !important;" /></a></span></div></div><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">看到了么？这是一个枚举类型&#8230;&#8230;连class都不用了，极简。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">由于创建枚举实例的过程是线程安全的，所以这种写法也没有同步的问题。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">作者对这个方法的评价：</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">这种写法在功能上与共有域方法相近，但是它更简洁，无偿地提供了序列化机制，绝对防止对此实例化，即使是在面对复杂的序列化或者反射攻击的时候。虽然这中方法还没有广泛采用，但是单元素的枚举类型已经成为实现Singleton的最佳方法。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">枚举单例这种方法问世一些，许多分析文章都称它是实现单例的最完美方法&#8212;&#8212;写法超级简单，而且又能解决大部分的问题。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">不过我个人认为这种方法虽然很优秀，但是它仍然不是完美的&#8212;&#8212;比如，在需要继承的场景，它就不适用了。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">6. 总结</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">OK，看到这里，你还会觉得单例模式是最简单的设计模式了么？再回头看一下你之前代码中的单例实现，觉得是无懈可击的么？</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">可能我们在实际的开发中，对单例的实现并没有那么严格的要求。比如，我如果能保证所有的getInstance都是在一个线程的话，那其实第一种最简单的教科书方式就够用了。再比如，有时候，我的单例变成了多例也可能对程序没什么太大影响&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">但是，如果我们能了解更多其中的细节，那么如果哪天程序出了些问题，我们起码能多一个排查问题的点。早点解决问题，就能早点回家吃饭&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212; 还有，完美的方案是不存在，任何方式都会有一个『度』的问题。比如，你的觉得代码已经无懈可击了，但是因为你用的是JAVA语言，可能ClassLoader有些BUG啊&#8230;&#8230;你的代码谁运行在JVM上的，可能JVM本身有BUG啊&#8230;&#8230;你的代码运行在手机上，可能手机系统有问题啊&#8230;&#8230;你生活在这个宇宙里，可能宇宙本身有些BUG啊&#8230;&#8230;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">所以，尽力做到能做到的最好就行了。</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&nbsp;</p><p style="line-height: 1.5; margin-top: 10px; margin-bottom: 10px;">&#8212;&#8212; 感谢你花费了不少时间看到这里，但愿你没有觉得虚度。</p></div><div style="clear: both;"></div><div id="blog_post_info_block" style="margin-top: 20px;"><div id="BlogPostCategory" style="margin-bottom: 10px; color: #4b4b4b; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff;"></div><div id="EntryTag" style="color: #4b4b4b; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff; margin-top: 0px !important;"></div><div id="blog_post_info" style="color: #4b4b4b; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; background-color: #ffffff;"></div></div><img src ="http://www.blogjava.net/jlin/aggbug/433525.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jlin/" target="_blank">fly</a> 2018-11-27 22:51 <a href="http://www.blogjava.net/jlin/archive/2018/11/27/433525.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Java 中的双重检查（Double-Check)-转</title><link>http://www.blogjava.net/jlin/archive/2017/06/06/432584.html</link><dc:creator>fly</dc:creator><author>fly</author><pubDate>Tue, 06 Jun 2017 05:36:00 GMT</pubDate><guid>http://www.blogjava.net/jlin/archive/2017/06/06/432584.html</guid><wfw:comment>http://www.blogjava.net/jlin/comments/432584.html</wfw:comment><comments>http://www.blogjava.net/jlin/archive/2017/06/06/432584.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jlin/comments/commentRss/432584.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jlin/services/trackbacks/432584.html</trackback:ping><description><![CDATA[<p style="color: #333333; font-family: Arial; background-color: #ffffff;">在 Effecitve&nbsp;<a href="http://lib.csdn.net/base/java" title="Java 知识库" target="_blank" style="color: #df3434; text-decoration-line: none; font-weight: bold;">Java</a>&nbsp;一书的第 48 条中提到了双重检查模式，并指出这种模式在 Java 中通常并不适用。该模式的结构如下所示：</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 634px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;Resource&nbsp;getResource()&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;if&nbsp;(resource&nbsp;==&nbsp;null)&nbsp;{&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;synchronized(this){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resource==null)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resource&nbsp;=&nbsp;new&nbsp;Resource();&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;return&nbsp;resource;&nbsp;&nbsp;</li><li style="line-height: 18px;">}&nbsp;&nbsp;</li></ol></div><p>&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">该模式是对下面的代码改进：</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 949px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;synchronized&nbsp;Resource&nbsp;getResource(){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;if&nbsp;(resource&nbsp;==&nbsp;null){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resource&nbsp;=&nbsp;new&nbsp;Resource();&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;return&nbsp;resource;&nbsp;&nbsp;</li><li style="line-height: 18px;">}&nbsp;&nbsp;</li></ol></div><p>&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">这段代码的目的是对 resource 延迟初始化。但是每次访问的时候都需要同步。为了减少同步的开销，于是有了双重检查模式。</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">在 Java 中双重检查模式无效的原因是在不同步的情况下引用类型不是线程安全的。对于除了 long 和 double 的基本类型，双重检查模式是适用 的。比如下面这段代码就是正确的：</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 1284px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">private&nbsp;int&nbsp;count;&nbsp;&nbsp;</li><li style="line-height: 18px;">public&nbsp;int&nbsp;getCount(){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;if&nbsp;(count&nbsp;==&nbsp;<span style="color: #c00000;">0</span>){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;synchronized(this){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(count&nbsp;==&nbsp;<span style="color: #c00000;">0</span>){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count&nbsp;=&nbsp;computeCount();&nbsp;&nbsp;//一个耗时的计算&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;return&nbsp;count;&nbsp;&nbsp;</li><li style="line-height: 18px;">}&nbsp;&nbsp;</li></ol></div><p>&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">上面就是关于java中双重检查模式（double-check idiom）的一般结论。但是事情还没有结束，因为java的内存模式也在改进中。Doug Lea 在他的文章中写道：&#8220;根据最新的 JSR133 的 Java 内存模型，如果将引用类型声明为 volatile，双重检查模式就可以工作了&#8221;，参见&nbsp;<a href="http://gee.cs.oswego.edu/dl/cpj/updates.html" target="_blank" style="color: #336699; text-decoration-line: none;">http://gee.cs.oswego.edu/dl/cpj/updates.html</a>&nbsp;。</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">所以以后要在 Java 中使用双重检查模式，可以使用下面的代码：</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 1749px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">private&nbsp;volatile&nbsp;Resource&nbsp;resource;&nbsp;&nbsp;</li><li style="line-height: 18px;">public&nbsp;Resource&nbsp;getResource(){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;if&nbsp;(resource&nbsp;==&nbsp;null){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;synchronized(this){&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(resource==null){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resource&nbsp;=&nbsp;new&nbsp;Resource();&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;return&nbsp;resource;&nbsp;&nbsp;</li><li style="line-height: 18px;">}&nbsp;&nbsp;</li></ol></div><p>&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">当然了，得是在遵循 JSR133 规范的 Java 中。</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">所以，double-check 在 J2SE 1.4 或早期版本在多线程或者 JVM 调优时由于 out-of-order writes，是不可用的。 这个问题在 J2SE 5.0 中已经被修复，可以使用 volatile 关键字来保证多线程下的单例。</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 2148px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;class&nbsp;Singleton&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;volatile&nbsp;Singleton&nbsp;instance&nbsp;=&nbsp;null;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;Singleton&nbsp;getInstance()&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(instance&nbsp;==&nbsp;null)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;synchronized(this)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(instance&nbsp;==&nbsp;null)&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;instance&nbsp;=&nbsp;new&nbsp;Singleton();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;instance;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">}&nbsp;&nbsp;</li></ol></div><p>&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"><strong>推荐方法</strong>&nbsp;是Initialization on Demand Holder（IODH），</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">详见&nbsp;<a href="http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom" target="_blank" style="color: #336699; text-decoration-line: none;">http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom</a></p><p style="color: #333333; font-family: Arial; background-color: #ffffff;">&nbsp;</p><p style="color: #333333; font-family: Arial; background-color: #ffffff;"></p><div bg_java"="" style="width: 700.906px; overflow-y: hidden; position: relative;"><div><div><strong>[java]</strong>&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="view plain" target="_blank" style="background-image: url(&quot;images/default/ico_plain.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">view plain</a><span data-mod="popu_168">&nbsp;<a href="http://blog.csdn.net/dl88250/article/details/5439024#" title="copy" target="_blank" style="background-image: url(&quot;images/default/ico_copy.gif&quot;); background-position: left top; background-repeat: no-repeat; padding: 1px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">copy</a><div style="position: absolute; left: 563px; top: 2597px; width: 18px; height: 18px; z-index: 99;"></div></span></div></div><ol start="1"><li style="line-height: 18px;">public&nbsp;class&nbsp;Singleton&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;class&nbsp;SingletonHolder&nbsp;{&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;Singleton&nbsp;instance&nbsp;=&nbsp;new&nbsp;Singleton();&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;Singleton&nbsp;getInstance(){&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;SingletonHolder.instance;&nbsp;&nbsp;</li><li style="line-height: 18px;">&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</li><li style="line-height: 18px;">} &nbsp;</li></ol></div><p>&nbsp;</p><img src ="http://www.blogjava.net/jlin/aggbug/432584.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jlin/" target="_blank">fly</a> 2017-06-06 13:36 <a href="http://www.blogjava.net/jlin/archive/2017/06/06/432584.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式学习笔记-观察者模式(转)</title><link>http://www.blogjava.net/jlin/archive/2013/08/11/402673.html</link><dc:creator>fly</dc:creator><author>fly</author><pubDate>Sun, 11 Aug 2013 15:41:00 GMT</pubDate><guid>http://www.blogjava.net/jlin/archive/2013/08/11/402673.html</guid><wfw:comment>http://www.blogjava.net/jlin/comments/402673.html</wfw:comment><comments>http://www.blogjava.net/jlin/archive/2013/08/11/402673.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jlin/comments/commentRss/402673.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jlin/services/trackbacks/402673.html</trackback:ping><description><![CDATA[<h1 class="postTitle"><a id="cb_post_title_url" class="postTitle2" href="http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html">设计模式学习笔记-观察者模式</a> </h1>
<div class="clear"></div>
<div class="postBody">
<div id="cnblogs_post_body">
<p>1. 概述</p>
<p>　　有时被称作发布/订阅模式，观察者模式定义了一种一对多的依赖关系，让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时，会通知所有观察者对象，使它们能够自动更新自己。</p>
<p>2. 解决的问题</p>
<p>　　将一个系统分割成一个一些类相互协作的类有一个不好的副作用，那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合，这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。</p>
<p>3. 模式中的角色</p>
<p>　　3.1 抽象主题（Subject）：它把所有观察者对象的引用保存到一个聚集里，每个主题都可以有任何数量的观察者。抽象主题提供一个接口，可以增加和删除观察者对象。</p>
<p>　　3.2 具体主题（ConcreteSubject）：将有关状态存入具体观察者对象；在具体主题内部状态改变时，给所有登记过的观察者发出通知。</p>
<p>　　3.3 抽象观察者（Observer）：为所有的具体观察者定义一个接口，在得到主题通知时更新自己。</p>
<p>　　3.4 具体观察者（ConcreteObserver）：实现抽象观察者角色所要求的更新接口，以便使本身的状态与主题状态协调。</p>
<p>4. 模式解读</p>
<p>　　4.1&nbsp;观察者模式的类图　　</p>
<p>　　<img alt="" src="http://pic002.cnblogs.com/images/2012/155937/2012071215463875.png" /></p>
<p>　　4.2&nbsp;观察者模式的代码</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 抽象主题类
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">abstract</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Subject
    {
        </span><span style="color: #0000ff">private</span> IList&lt;Observer&gt; observers = <span style="color: #0000ff">new</span> List&lt;Observer&gt;<span style="color: #000000">();

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 增加观察者
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #808080">///</span> <span style="color: #808080">&lt;param name="observer"&gt;&lt;/param&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Attach(Observer observer)
        {
            observers.Add(observer);
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 移除观察者
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #808080">///</span> <span style="color: #808080">&lt;param name="observer"&gt;&lt;/param&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Detach(Observer observer)
        {
            observers.Remove(observer);
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 向观察者（们）发出通知
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Notify()
        {
            </span><span style="color: #0000ff">foreach</span> (Observer o <span style="color: #0000ff">in</span><span style="color: #000000"> observers)
            {
                o.Update();
            }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 抽象观察者类，为所有具体观察者定义一个接口，在得到通知时更新自己
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">abstract</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Observer
    {
        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">abstract</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update();
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 具体观察者或具体通知者，将有关状态存入具体观察者对象；在具体主题的内部状态改变时，给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> ConcreteSubject : Subject
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> subjectState;

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 具体观察者的状态
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span><span style="color: #000000"> SubjectState
        {
            </span><span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span><span style="color: #000000"> subjectState; }
            </span><span style="color: #0000ff">set</span> { subjectState =<span style="color: #000000"> value; }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 具体观察者，实现抽象观察者角色所要求的更新接口，已是本身状态与主题状态相协调
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> ConcreteObserver : Observer
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> observerState;
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> name;
        </span><span style="color: #0000ff">private</span><span style="color: #000000"> ConcreteSubject subject;

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 具体观察者用一个具体主题来实现
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span><span style="color: #000000"> ConcreteSubject Subject
        {
            </span><span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span><span style="color: #000000"> subject; }
            </span><span style="color: #0000ff">set</span> { subject =<span style="color: #000000"> value; }
        }

        </span><span style="color: #0000ff">public</span> ConcreteObserver(ConcreteSubject subject, <span style="color: #0000ff">string</span><span style="color: #000000"> name)
        {
            </span><span style="color: #0000ff">this</span>.subject =<span style="color: #000000"> subject;
            </span><span style="color: #0000ff">this</span>.name =<span style="color: #000000"> name;
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 实现抽象观察者中的更新操作
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update()
        {
            observerState </span>=<span style="color: #000000"> subject.SubjectState;
            Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">The observer's state of {0} is {1}</span><span style="color: #800000">"</span><span style="color: #000000">, name, observerState);
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　4.3&nbsp;客户端代码</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #0000ff">class</span><span style="color: #000000"> Program
    {
        </span><span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> Main(<span style="color: #0000ff">string</span><span style="color: #000000">[] args)
        {
            </span><span style="color: #008000">//</span><span style="color: #008000"> 具体主题角色通常用具体自来来实现</span>
            ConcreteSubject subject = <span style="color: #0000ff">new</span><span style="color: #000000"> ConcreteSubject();

            subject.Attach(</span><span style="color: #0000ff">new</span> ConcreteObserver(subject, <span style="color: #800000">"</span><span style="color: #800000">Observer A</span><span style="color: #800000">"</span><span style="color: #000000">));
            subject.Attach(</span><span style="color: #0000ff">new</span> ConcreteObserver(subject, <span style="color: #800000">"</span><span style="color: #800000">Observer B</span><span style="color: #800000">"</span><span style="color: #000000">));
            subject.Attach(</span><span style="color: #0000ff">new</span> ConcreteObserver(subject, <span style="color: #800000">"</span><span style="color: #800000">Observer C</span><span style="color: #800000">"</span><span style="color: #000000">));

            subject.SubjectState </span>= <span style="color: #800000">"</span><span style="color: #800000">Ready</span><span style="color: #800000">"</span><span style="color: #000000">;
            subject.Notify();

            Console.Read();
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　运行结果</p>
<p>　　<img alt="" src="http://pic002.cnblogs.com/images/2012/155937/2012071213265288.png" /></p>
<p>5. 模式总结</p>
<p>　　5.1 优点</p>
<p>　　　　5.1.1 观察者模式解除了主题和具体观察者的耦合，让耦合的双方都依赖于抽象，而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。</p>
<p>　　5.2 缺点</p>
<p>　　　　5.2.1 依赖关系并未完全解除，抽象通知者依旧依赖抽象的观察者。</p>
<p>　　5.3 适用场景</p>
<p>　　　　5.3.1 当一个对象的改变需要给变其它对象时，而且它不知道具体有多少个对象有待改变时。</p>
<p>　　　　5.3.2 一个抽象某型有两个方面，当其中一个方面依赖于另一个方面，这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。</p>
<p>&nbsp;</p>
<p>6. 模式引申，应用C#中的事件委托来彻底解除通知者和观察者之间的耦合。</p>
<p>　　　6.1 关于委托的定义：委托是一种引用方法的类型。一旦为委托分配了方法，委托将与该方法有相同的行为。委托方法可以像其它任何方法一样，具有参数和返回值。委托可以看作是对函数（方法）的的抽象，是函数的&#8220;类&#8221;，委托的实例代表一个（或多个）具体的函数，它可以是多播的。</p>
<p>　　　6.2 关于事件：事件基于委托，为委托提供了一种发布/订阅机制。事件的订阅与取消与我们刚才讲的观察者模式中的订阅与取消类似，只是表现形式有所不同。在观察者模式中，订阅使用方法Attach（）来进行；在事件的订阅中使用&#8220;+=&#8221;。类似地，取消订阅在观察者模式中用Dettach（），而事件的取消用&#8220;-=&#8221;。</p>
<p>&nbsp;</p>
<p>7. 下面例子分别用观察者模式，事件机制来实现</p>
<p>　　7.1 实例描述：客户支付了订单款项，这时财务需要开具发票，出纳需要记账，配送员需要配货。</p>
<p>　　7.2 观察者模式的实现</p>
<p>　　　　7.2.1 类图</p>
<p>　　　　<img alt="" src="http://pic002.cnblogs.com/images/2012/155937/2012071215531341.png" /></p>
<p>　　　　7.2.2 代码实现</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 抽象观察者
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> ISubject
    {
        </span><span style="color: #0000ff">void</span><span style="color: #000000"> Notify();
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 工作岗位，作为这里的观察者的抽象
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">abstract</span> <span style="color: #0000ff">class</span><span style="color: #000000"> JobStation
    {
        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">abstract</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update();
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 具体主题，这里是客户
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Customer : ISubject
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> customerState;

        </span><span style="color: #0000ff">private</span> IList&lt;JobStation&gt; observers = <span style="color: #0000ff">new</span> List&lt;JobStation&gt;<span style="color: #000000">();

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 增加观察者
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #808080">///</span> <span style="color: #808080">&lt;param name="observer"&gt;&lt;/param&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Attach(JobStation observer)
        {
            </span><span style="color: #0000ff">this</span><span style="color: #000000">.observers.Add(observer);
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 移除观察者
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #808080">///</span> <span style="color: #808080">&lt;param name="observer"&gt;&lt;/param&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Detach(JobStation observer)
        {
            </span><span style="color: #0000ff">this</span><span style="color: #000000">.observers.Remove(observer);
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 客户状态
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span><span style="color: #000000"> CustomerState
        {
            </span><span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span><span style="color: #000000"> customerState; }
            </span><span style="color: #0000ff">set</span> { customerState =<span style="color: #000000"> value; }
        }

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Notify()
        {
            </span><span style="color: #0000ff">foreach</span> (JobStation o <span style="color: #0000ff">in</span><span style="color: #000000"> observers)
            {
                o.Update();
            }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 会计
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Accountant : JobStation
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> accountantState;
        </span><span style="color: #0000ff">private</span><span style="color: #000000"> Customer customer;

        </span><span style="color: #0000ff">public</span><span style="color: #000000"> Accountant(Customer customer)
        {
            </span><span style="color: #0000ff">this</span>.customer =<span style="color: #000000"> customer;
        }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 更新状态
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update()
        {
            </span><span style="color: #0000ff">if</span> (customer.CustomerState == <span style="color: #800000">"</span><span style="color: #800000">已付款</span><span style="color: #800000">"</span><span style="color: #000000">)
            {
                Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是会计，我来开具发票。</span><span style="color: #800000">"</span><span style="color: #000000">);
                accountantState </span>= <span style="color: #800000">"</span><span style="color: #800000">已开发票</span><span style="color: #800000">"</span><span style="color: #000000">;
            }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 出纳
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Cashier : JobStation
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> cashierState;
        </span><span style="color: #0000ff">private</span><span style="color: #000000"> Customer customer;

        </span><span style="color: #0000ff">public</span><span style="color: #000000"> Cashier(Customer customer)
        {
            </span><span style="color: #0000ff">this</span>.customer =<span style="color: #000000"> customer;
        }

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update()
        {
            </span><span style="color: #0000ff">if</span> (customer.CustomerState == <span style="color: #800000">"</span><span style="color: #800000">已付款</span><span style="color: #800000">"</span><span style="color: #000000">)
            {
                Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是出纳员，我给登记入账。</span><span style="color: #800000">"</span><span style="color: #000000">);
                cashierState </span>= <span style="color: #800000">"</span><span style="color: #800000">已入账</span><span style="color: #800000">"</span><span style="color: #000000">;
            }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 配送员
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Dilliveryman : JobStation
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> dillivierymanState;
        </span><span style="color: #0000ff">private</span><span style="color: #000000"> Customer customer;

        </span><span style="color: #0000ff">public</span><span style="color: #000000"> Dilliveryman(Customer customer)
        {
            </span><span style="color: #0000ff">this</span>.customer =<span style="color: #000000"> customer;
        }

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Update()
        {
            </span><span style="color: #0000ff">if</span> (customer.CustomerState == <span style="color: #800000">"</span><span style="color: #800000">已付款</span><span style="color: #800000">"</span><span style="color: #000000">)
            {
                Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是配送员，我来发货。</span><span style="color: #800000">"</span><span style="color: #000000">);
                dillivierymanState </span>= <span style="color: #800000">"</span><span style="color: #800000">已发货</span><span style="color: #800000">"</span><span style="color: #000000">;
            }
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　　　7.2.3 客户端代码</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #0000ff">class</span><span style="color: #000000"> Program
    {
        </span><span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> Main(<span style="color: #0000ff">string</span><span style="color: #000000">[] args)
        {

            Customer subject </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Customer();

            subject.Attach(</span><span style="color: #0000ff">new</span><span style="color: #000000"> Accountant(subject));
            subject.Attach(</span><span style="color: #0000ff">new</span><span style="color: #000000"> Cashier(subject));
            subject.Attach(</span><span style="color: #0000ff">new</span><span style="color: #000000"> Dilliveryman(subject));

            subject.CustomerState </span>= <span style="color: #800000">"</span><span style="color: #800000">已付款</span><span style="color: #800000">"</span><span style="color: #000000">;
            subject.Notify();

            Console.Read();
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　　　运行结果：</p>
<p>　　　　<span style="background-color: #00ffff">我是会计，我来开具发票。</span><br />　　　　<span style="background-color: #00ffff">我是出纳员，我给登记入账。</span><br />　　　　<span style="background-color: #00ffff">我是配送员，我来发货。</span></p>
<p>&nbsp;</p>
<p>　　7.3 事件实现</p>
<p>　　　　7.3.1 类图</p>
<p>　　　　<img alt="" src="http://pic002.cnblogs.com/images/2012/155937/2012071216194626.png" /></p>
<p>　　　　通过类图来看，观察者和主题之间已经不存在任何依赖关系了。</p>
<p>　　　　7.3.2 代码实现</p>
<p>　　　　</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 抽象主题
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">interface</span><span style="color: #000000"> ISubject
    {
        </span><span style="color: #0000ff">void</span><span style="color: #000000"> Notify();
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 声明委托
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">delegate</span> <span style="color: #0000ff">void</span><span style="color: #000000"> CustomerEventHandler();

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 具体主题
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Customer : ISubject
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> customerState;

        </span><span style="color: #008000">//</span><span style="color: #008000"> 声明一个委托事件，类型为 CustomerEventHandler</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">event</span><span style="color: #000000"> CustomerEventHandler Update;

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Notify()
        {
            </span><span style="color: #0000ff">if</span> (Update != <span style="color: #0000ff">null</span><span style="color: #000000">)
            {
                </span><span style="color: #008000">//</span><span style="color: #008000"> 使用事件来通知给订阅者</span>
<span style="color: #000000">                Update();
            }
        }

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">string</span><span style="color: #000000"> CustomerState
        {
            </span><span style="color: #0000ff">get</span> { <span style="color: #0000ff">return</span><span style="color: #000000"> customerState; }
            </span><span style="color: #0000ff">set</span> { customerState =<span style="color: #000000"> value; }
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 财务，已经不需要实现抽象的观察者类，并且不用引用具体的主题
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Accountant
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> accountantState;

        </span><span style="color: #0000ff">public</span><span style="color: #000000"> Accountant()
        { }

        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
        <span style="color: #808080">///</span><span style="color: #008000"> 开发票
        </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
        <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> GiveInvoice()
        {
            Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是会计，我来开具发票。</span><span style="color: #800000">"</span><span style="color: #000000">);
            accountantState </span>= <span style="color: #800000">"</span><span style="color: #800000">已开发票</span><span style="color: #800000">"</span><span style="color: #000000">;
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 出纳，已经不需要实现抽象的观察者类，并且不用引用具体的主题
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Cashier
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> cashierState;

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Recoded()
        {
            Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是出纳员，我给登记入账。</span><span style="color: #800000">"</span><span style="color: #000000">);
            cashierState </span>= <span style="color: #800000">"</span><span style="color: #800000">已入账</span><span style="color: #800000">"</span><span style="color: #000000">;
        }
    }

    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;summary&gt;</span>
    <span style="color: #808080">///</span><span style="color: #008000"> 配送员，已经不需要实现抽象的观察者类，并且不用引用具体的主题
    </span><span style="color: #808080">///</span> <span style="color: #808080">&lt;/summary&gt;</span>
    <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> Dilliveryman
    {
        </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">string</span><span style="color: #000000"> dillivierymanState;

        </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span><span style="color: #000000"> Dilliver()
        {
            Console.WriteLine(</span><span style="color: #800000">"</span><span style="color: #800000">我是配送员，我来发货。</span><span style="color: #800000">"</span><span style="color: #000000">);
            dillivierymanState </span>= <span style="color: #800000">"</span><span style="color: #800000">已发货</span><span style="color: #800000">"</span><span style="color: #000000">;
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　　　7.3.3 客户端代码</p>
<div class="cnblogs_code">
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div><pre>    <span style="color: #0000ff">class</span><span style="color: #000000"> Program
    {
        </span><span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> Main(<span style="color: #0000ff">string</span><span style="color: #000000">[] args)
        {

            Customer subject </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Customer();

            Accountant accountant </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Accountant();
            Cashier cashier </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Cashier();
            Dilliveryman dilliveryman </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Dilliveryman();

            </span><span style="color: #008000">//</span><span style="color: #008000"> 注册事件</span>
            subject.Update +=<span style="color: #000000"> accountant.GiveInvoice;
            subject.Update </span>+=<span style="color: #000000"> cashier.Recoded;
            subject.Update </span>+=<span style="color: #000000"> dilliveryman.Dilliver;

            </span><span style="color: #008000">/*</span><span style="color: #008000">
             * 以上写法也可以用下面代码来替换
            subject.Update += new CustomerEventHandler(accountant.GiveInvoice);
            subject.Update += new CustomerEventHandler(cashier.Recoded);
            subject.Update += new CustomerEventHandler(dilliveryman.Dilliver);
             </span><span style="color: #008000">*/</span><span style="color: #000000">

            subject.CustomerState </span>= <span style="color: #800000">"</span><span style="color: #800000">已付款</span><span style="color: #800000">"</span><span style="color: #000000">;
            subject.Notify();

            Console.Read();
        }
    }</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a title="复制代码" href="javascript:void(0);"><img alt="复制代码" src="http://common.cnblogs.com/images/copycode.gif" /></a></span></div></div>
<p>　　　　运行结果</p>
<p><span>　　　　<span style="background-color: #00ffff">我是会计，我来开具发票。</span></span><br />　　　　<span style="background-color: #00ffff">我是出纳员，我给登记入账。</span><br />　　　　<span style="background-color: #00ffff">我是配送员，我来发货。</span></p></div></div> <img src ="http://www.blogjava.net/jlin/aggbug/402673.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jlin/" target="_blank">fly</a> 2013-08-11 23:41 <a href="http://www.blogjava.net/jlin/archive/2013/08/11/402673.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>