`
BestUpon
  • 浏览: 284301 次
  • 性别: Icon_minigender_1
  • 来自: 兰州
社区版块
存档分类
最新评论

单例模式(Singleton)

阅读更多

首先我们先看看一个例子,在说什么是单利模式(Singleton):

package org.bestupon.dp.singleton;
/**
* 
* @author BestUpon
* @email bestupon@foxmail.com
* @date 2010-6-13上午11:08:28
* @ask jdk中Runtime这个类似就是一个单例模式的应用:
* 
* @answer
*/
public class Test4RunTime {

public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
runtime.freeMemory();
}
}

上面的例子可以看出在没有使用new,却获得了一个Runtime对象,这是为什么呢?让我们看看java.lang.Runtime.java的源码到底是怎么一回事:

public class Runtime { 
    private static Runtime currentRuntime = new Runtime(); 
    public static Runtime getRuntime() { 
        return currentRuntime; 
    } 
   /** Don't let anyone else instantiate this class */ 
   private Runtime() {} 
    // 以下略 
}

 以上是Runtime.java开头部分的代码,我们可以很清楚的看见,一开头就直接 new Runtime(), 一个对象,并且是静态的。在getRuntime()的时候,

直接将其返回给请求的客户端。 上面结构即采用Singleton模式设计,其结构使用 UML 描述如下所示:


Singleton的英文意义是独身,也就是只有一个人,应用在面向对象语言上,通常翻译作单例:单一个实例(Instance)。Singleton模式可以保证一个类别 只有一个实例,并只提供一个访问(visit)这个实例的方法。

1.定义:单例模式就是确保一个类中只有一个实例,并且该实例必须自动创建,并向整个系统提供该实例。

2.使用时机:当系统要求一个类只有一个实例时,就需要使用用单例模式。

 

有几个实例上面结构的方法,可以在第一次需要实例时再建立对象,也就是采用所谓的Lazy Initialization

 

public class Singleton { 
    private static Singleton instance = null; 
    private Singleton() { 
        // .... 
    } 
    public static Singleton getInstance() { 
        if (instance == null) {
            instance = new Singleton(); 
        }
        return instance; 
    } 

    // .. 其它实例 
}

 上面的实例适用于单线程的程序,在多线程的程序下,以下的写法在多个线程的竞争资源下,将仍有可能产生两个以上的实例,例如下面的情况:

 

Thread1: if(instance == null) // true
Thread2: if(instance == null) // true

Thread1: instance = new Singleton(); // 产生一个实例
Thread2: instance = new Singleton(); // 又产生一个实例

Thread1: return instance; // 回传一个实例
Thread2: return instance; // 又回传一个实例

 在多线程的环境下,为了避免资源同时竞争而导致如上产生多个实例的情况,加上同步(synchronized)机制:

 

public class Singleton {
      private static Singleton instance = null;
      private Singleton(){}
      synchronized static public Singleton getInstance() {
          if (instance == null) {
              instance = new Singleton();
          }
          return instance;
      }
  }

 不过这种简单的写法不适合用于像服务器这种服务很多线程的程序上,同步机制会造成相当的效能低落,为了顾及SingletonLazy Initialization与效能问题,因而有了Double-check Locking的模式:

 

public class Singleton {
      private static Singleton instance = null;
      private Singleton(){}
      public static Singleton getInstance() {
          if (instance == null){
              synchronized(Singleton.class){
                  if(instance == null) {
                       instance = new Singleton();
                  }
              }
          }
          return instance;
      }
  }

 也就是只有在第一次建立实例时才会进入同步区,之后由于实例已建立,也就不用进入同步区进行锁定。JavaRuntime类别的作法简单的多, 它舍弃了Lazy Initialization,如果您要取得单例的机会不是很多,可以用这种方式:

 

public class Singleton { 
    private static Singleton instance = new Singleton(); 
    private Singleton() { 
        // .... 
    } 
    public static Singleton getInstance() { 
        return instance; 
    } 
    // 其它实例 
}

 Singleton本身的观念简单但应用 很广,因而很多时候必须对实际环境作一些考虑与调整。

3.总结:

单例模式可以分为两种:饿汉式和懒汉式两种,饿汉是在系统启动的一开始就初始化好了实例,而懒汉式是在第一次访问的时候才初始化实例。

 

package org.bestupon.dp.singleton;

/**
 * @author BestUpon
 * @email bestupon@foxmail.com
 * @date 2010-6-13上午11:34:27
 * @ask 饿汉式单利模式
 * @answer
 */
public class HungerSingleton {

	/**
	 * 一开始就初始化好了实例
	 */
	private static HungerSingleton instance = new HungerSingleton();

	private HungerSingleton() {
	}

	public static HungerSingleton getInstance() {
		return instance;
	}

}

 package org.bestupon.dp.singleton;

/**
 * 
 * @author BestUpon
 * @email bestupon@foxmail.com
 * @date 2010-6-13上午11:41:22
 * @ask 懒汉式单例模式
 * @answer
 */
public class LazySingleton {
	private static LazySingleton instance = null;

	private LazySingleton() {
	}

	public static LazySingleton getInstance() {
		if(instance == null){
			instance = new LazySingleton();
		}
		return instance;
	}
}

4.优点:

在单利模式中,客户调用类的实例时,只能调用一个公共的接口,这就为整个开发团队提供了共享的概念,

5.缺点:

单利模式在实例化后,是不允许类的继承的;在分布式系统中,当系统的单利模式类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着单例对象是很困难的,而且单例类是很难实现序列化的。

 

 

 

更新(纠正)2010-6-17:

 

由于端午节放假的缘故,各位同仁在期间提出了很多的问题,也指出了我思维的局限性,没有及时的更新于回复各位好友的,深表遗憾!


针对单例模式的很多用法,前面一直是在所单一线程的问题,本来设计多线程的问题就很少,也对双重锁定这个问题没有深入的五挖掘,导致了犯了今天这样的错误。之后参考了一些资料,针对各位朋友提出的问题与我自身存在的问题,进行改进!参考的文章是:IBMDeveloperWorks(中国)的网站上的文章双重检查锁定及单例模式http://www.ibm.com/developerworks/cn/java/j-dcl.html)。这篇文章真针对各种问题都有分析,包括可见性等问题。得出了一个结论:双重锁定失效的主要原因是:不同JVM之间的无序写入问题,多线程之间的独占、休眠(记忆复苏)所引起的不同不问题。

最终本文提出了一个建议:议不要使用“双重锁定”!一个解决单例模式的方案:

底线就是:无论以何种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。因此,您有两种选择:

  • 接受如清单 2 中所示的 getInstance() 方法的同步。
  • 放弃同步,而使用一个 static 字段。

选择项 2 如清单 10 中所示:

 

/**
*使用 static 字段的单例实现
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();

  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}
 

 

再次感谢各位朋友的关注!

 

  • 大小: 10 KB
分享到:
评论
26 楼 i2534 2010-11-10  
Enum.instance
25 楼 gghaomm 2010-11-10  
大家都打个例子 自己试下就知道执行顺序了。
24 楼 cremains 2010-09-25  
挺不错的,学习了
23 楼 snake1987 2010-08-27  
myumen 写道
搜索到个更牛的写法,既满足Lazy load,又满足只有一个实例,即使多线程环境也是一个实例。
public class Singleton {     
    
    static class SingletonHolder {     
    
        static Singleton instance = new Singleton();     
    
    }     
    
    public static Singleton getInstance() {     
    
        return SingletonHolder.instance;     
    
    }     
}


怎么还这么多人不知道这个?老早就在je看到过了
参见http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom
个人理解:
原因:类在调用getInstance方法才会初始化(并非实例化)SingletonHolder,并且该过程是由jvm来保证其单线程执行的
sun的初始化的描述
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
T is a class and a static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant variable (§4.12.4).
T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.
22 楼 wa20463165 2010-08-26  
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">“new Singleton()不是一个atomic的操作。 ”<br><br>这样的操作在java中多么?<br>
</div>
<div class="quote_title"><br></div>
<p><span style="color: #ff0000;"><strong>为什么这个能保证延迟加载呢?</strong></span></p>
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<pre name="code" class="java">/**
*<span style="line-height: 19px; font-family: verdana,nsimsun,sans-serif; white-space: normal;"><a style="color: #5c81a7;" name="code10"><strong>使用 static 字段的单例实现</strong></a></span>
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();<span style="color: #ff0000;"><strong>//这个为什么能保证延迟加载呢》?</strong></span>
  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}</pre>
<p> </p>
</div>
</div>
<p> </p>
<p> </p>
<p>这个不能保证是延迟加载,而是饿汉方式!是为了解决双重锁定问题!</p>
</div>
<p>那么这个 为什么能呢?(我是提问的语气哦。。。不是反问的语气 - =)</p>
<pre name="code" class="java">public class Singleton {    
   
    static class SingletonHolder {    
   
        static Singleton instance = new Singleton();    
   
    }    
   
    public static Singleton getInstance() {    
   
        return SingletonHolder.instance;    
   
    }    
}
</pre>
</div>
<p> </p>
<p> </p>
<p>你说的这种写法我也是第一次见:我的理解是这样的</p>
<p> </p>
<pre name="code" class="java">public class Singleton {    
   
    static class SingletonHolder {    //静态内部类
   
        static Singleton instance = new Singleton();    
   
    }    
   
    public static Singleton getInstance() {    
   
        return SingletonHolder.instance;    
   
    }    
}</pre>
<p> 静态类在一开始就初始化好了,并且该静态类包含在<span style="white-space: pre;">Singleton这个类中,调用</span><span style="white-space: pre;"> getInstance()的时候返回的是</span><span style="white-space: pre;">SingletonHolder.instance;这个过程是相对而言的,意思是说,</span><span style="white-space: pre;">Singleton 这个类没有在初始化的时候</span></p>
<p>创建对象,而是创建了一个内部类的<span style="white-space: pre;">SingletonHolder,让他去创建</span><span style="white-space: pre;">Singleton的一个对象,这样在思维和逻辑上,</span><span style="white-space: pre;">Singleton</span><span style="white-space: pre;">在初始化的时候没有创建对象,而是在get的时候才创建对象的。</span></p>
<p>我觉得是相对而言,思维上的理解,不是绝对的没有创建对象,也不是绝对的延迟加载!不知道我理解的对不对!还望大家指教!</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<div><br></div>
<p> </p>
</div>
<p>我认为应该是延迟加载(当然我不知道对不对,如果错了,请大家指出来):首先jvm在启动的时候不会加载 SingletonHolder 这个静态内部类,也就说程序先执行</p>
<p>getInstance(),然后转到 SingletonHolder.instance()里面,然后方法里面有 new关键字,也就是jvm在这个时候加载 Singleton ,所以我觉得应该是延迟加载</p>
<p> </p>
21 楼 asnl 2010-06-18  
beneo 写道
myumen 写道
搜索到个更牛的写法,既满足Lazy load,又满足只有一个实例,即使多线程环境也是一个实例。
public class Singleton {     
    
    static class SingletonHolder {     
    
        static Singleton instance = new Singleton();     
    
    }     
    
    public static Singleton getInstance() {     
    
        return SingletonHolder.instance;     
    
    }     
}


这个的确牛

Apache就是这么实现的,不过SingletonHolder是私有内部静态类...
20 楼 抛出异常的爱 2010-06-17  
由于classloader使用了一次而不是二次.
当然如果classloader使用了二次.....这个也是不安全的.
19 楼 BestUpon 2010-06-17  
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">“new Singleton()不是一个atomic的操作。 ”<br><br>这样的操作在java中多么?<br>
</div>
<div class="quote_title"><br></div>
<p>
<span style="color: #ff0000;"><strong>为什么这个能保证延迟加载呢?</strong></span></p>
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<pre name="code" class="java">/**
*<span style="white-space: normal; line-height: 19px; font-family: verdana,nsimsun,sans-serif;"><a style="color: #5c81a7;" name="code10"><strong>使用 static 字段的单例实现</strong></a></span>
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();<span style="color: #ff0000;"><strong>//这个为什么能保证延迟加载呢》?</strong></span>
  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}</pre>
<p> </p>
</div>
</div>
<p> </p>
<p> </p>
<p>这个不能保证是延迟加载,而是饿汉方式!是为了解决双重锁定问题!</p>
</div>
<p>那么这个 为什么能呢?(我是提问的语气哦。。。不是反问的语气 - =)</p>
<pre name="code" class="java">public class Singleton {    
   
    static class SingletonHolder {    
   
        static Singleton instance = new Singleton();    
   
    }    
   
    public static Singleton getInstance() {    
   
        return SingletonHolder.instance;    
   
    }    
}
</pre>
</div>
<p> </p>
<p> </p>
<p>你说的这种写法我也是第一次见:我的理解是这样的</p>
<p> </p>
<pre name="code" class="java">public class Singleton {    
   
    static class SingletonHolder {    //静态内部类
   
        static Singleton instance = new Singleton();    
   
    }    
   
    public static Singleton getInstance() {    
   
        return SingletonHolder.instance;    
   
    }    
}</pre>
<p> 静态类在一开始就初始化好了,并且该静态类包含在<span style="white-space: pre;">Singleton这个类中,调用</span><span style="white-space: pre;"> getInstance()的时候返回的是</span><span style="white-space: pre;">SingletonHolder.instance;这个过程是相对而言的,意思是说,</span><span style="white-space: pre;">Singleton 这个类没有在初始化的时候</span></p>
<p>创建对象,而是创建了一个内部类的<span style="white-space: pre;">SingletonHolder,让他去创建</span><span style="white-space: pre;">Singleton的一个对象,这样在思维和逻辑上,</span><span style="white-space: pre;">Singleton</span><span style="white-space: pre;">在初始化的时候没有创建对象,而是在get的时候才创建对象的。</span></p>
<p>我觉得是相对而言,思维上的理解,不是绝对的没有创建对象,也不是绝对的延迟加载!不知道我理解的对不对!还望大家指教!</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<div><br></div>
<p> </p>
18 楼 gstarwd 2010-06-17  
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">“new Singleton()不是一个atomic的操作。 ”<br><br>这样的操作在java中多么?<br>
</div>
<div class="quote_title"><br></div>
<p>
<span style="color: #ff0000;"><strong>为什么这个能保证延迟加载呢?</strong></span></p>
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<pre name="code" class="java">/**
*<span style="white-space: normal; line-height: 19px; font-family: verdana,nsimsun,sans-serif;"><a style="color: #5c81a7;" name="code10"><strong>使用 static 字段的单例实现</strong></a></span>
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();<span style="color: #ff0000;"><strong>//这个为什么能保证延迟加载呢》?</strong></span>
  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}</pre>
<p> </p>
</div>
</div>
<p> </p>
<p> </p>
<p>这个不能保证是延迟加载,而是饿汉方式!是为了解决双重锁定问题!</p>
</div>
<p>那么这个 为什么能呢?(我是提问的语气哦。。。不是反问的语气 - =)</p>
<pre name="code" class="java">public class Singleton {    
   
    static class SingletonHolder {    
   
        static Singleton instance = new Singleton();    
   
    }    
   
    public static Singleton getInstance() {    
   
        return SingletonHolder.instance;    
   
    }    
}
</pre>
17 楼 BestUpon 2010-06-17  
<div class="quote_title">gstarwd 写道</div>
<div class="quote_div">
<div class="quote_title">“new Singleton()不是一个atomic的操作。 ”<br><br>这样的操作在java中多么?<br>
</div>
<div class="quote_title"><br></div>
<p>
<span style="color: #ff0000;"><strong>为什么这个能保证延迟加载呢?</strong></span></p>
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<pre name="code" class="java">/**
*<span style="white-space: normal; line-height: 19px; font-family: verdana,nsimsun,sans-serif;"><a style="color: #5c81a7;" name="code10"><strong>使用 static 字段的单例实现</strong></a></span>
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();<span style="color: #ff0000;"><strong>//这个为什么能保证延迟加载呢》?</strong></span>
  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}</pre>
<p> </p>
</div>
</div>
<p> </p>
<p> </p>
<p>这个不能保证是延迟加载,而是饿汉方式!是为了解决双重锁定问题!</p>
16 楼 gstarwd 2010-06-17  
<div class="quote_title">“new Singleton()不是一个atomic的操作。 ”<br><br>这样的操作在java中多么?<br>
</div>
<div class="quote_title"><br></div>
<p>
<span style="color: #ff0000;"><strong>为什么这个能保证延迟加载呢?</strong></span></p>
<div class="quote_title">BestUpon 写道</div>
<div class="quote_div">
<pre name="code" class="java">/**
*<span style="white-space: normal; line-height: 19px; font-family: verdana,nsimsun,sans-serif;"><a style="color: #5c81a7;" name="code10"><strong>使用 static 字段的单例实现</strong></a></span>
**/
class Singleton
{
  private Vector v;
  private boolean inUse;
  private static Singleton instance = new Singleton();<span style="color: #ff0000;"><strong>//这个为什么能保证延迟加载呢》?</strong></span>
  private Singleton()
  {
    v = new Vector();
    inUse = true;
    //...
  }

  public static Singleton getInstance()
  {
    return instance;
  }
}</pre>
<p> </p>
</div>
15 楼 BestUpon 2010-06-17  
kraft 写道
如果singleton这个class里面的东东都是对象的方法或变量,就没有必要lazy了吧,必须通过getinstance来使用里面的方法,所以类加载时初始化就可以了,没人用到这个类自然不会加载,除非有其他的static成员

所谓的加载,是在程序一启动的时候加载还是在访问的时候加载!在程序一启动的时候就加载该类对象实例,该实例中包含的所有的有用信息,如果类很庞大的话,启动速度当然会很慢(对该类实例化耗费时间),而耗费了很大力气初始化好了该对象实例,但是用它的时候很迟很迟,可以说是在将来很长一段时间,都不会用到该类的实例,但是他却一直在contain中,是不是很耗资源呢?而在其访问的时候去加载它,虽然加载的时候会消耗一部分时间,但是换来了以前所有资源的节约,你说有没有必要实行延迟加载呢?
14 楼 BestUpon 2010-06-17  
silence1214 写道
double check出来很久了吧 怎么又搞出来了。

前车之鉴还是得学习的嘛!
13 楼 BestUpon 2010-06-17  
slaser 写道
LZ,你的double-checking lock 是错误的。
new Singleton()不是一个atomic的操作。
你的instance可能处于一个不是null,但是又不是完整对象的状态。
这样会导致程序崩溃。

你说的很对!我以前没有意识到这一点,多线程没有想到线程的中断和独占问题!谢谢你的提醒!
12 楼 BestUpon 2010-06-17  
Agrael 写道
双重锁定在java里一定要保证可见性,你的例子里没保证这一点额。

兄台说的可见性,是否是使用声明称“Volatile ”?
11 楼 kraft 2010-06-16  
如果singleton这个class里面的东东都是对象的方法或变量,就没有必要lazy了吧,必须通过getinstance来使用里面的方法,所以类加载时初始化就可以了,没人用到这个类自然不会加载,除非有其他的static成员
10 楼 silence1214 2010-06-16  
double check出来很久了吧 怎么又搞出来了。
9 楼 beneo 2010-06-15  
myumen 写道
搜索到个更牛的写法,既满足Lazy load,又满足只有一个实例,即使多线程环境也是一个实例。
public class Singleton {     
    
    static class SingletonHolder {     
    
        static Singleton instance = new Singleton();     
    
    }     
    
    public static Singleton getInstance() {     
    
        return SingletonHolder.instance;     
    
    }     
}


这个的确牛
8 楼 slaser 2010-06-15  
LZ,你的double-checking lock 是错误的。
new Singleton()不是一个atomic的操作。
你的instance可能处于一个不是null,但是又不是完整对象的状态。
这样会导致程序崩溃。
7 楼 vieri122 2010-06-15  
单列模式 ,需要释放吗??

相关推荐

Global site tag (gtag.js) - Google Analytics