首先我们先看看一个例子,在说什么是单利模式(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;
- }
- }
不过这种简单的写法不适合用于像服务器这种服务很多线程的程序上,同步机制会造成相当的效能低落,为了顾及
Singleton
、
Lazy
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;
- }
- }
也就是只有在第一次建立实例时才会进入同步区,之后由于实例已建立,也就不用进入同步区进行锁定。
Java
中
Runtime
类别的作法简单的多,
它舍弃了
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.缺点:
单利模式在实例化后,是不允许类的继承的;在分布式系统中,当系统的单利模式类被复制运行在多个虚拟机下时,在每一个虚拟机下都会创建一个实例对象,此时如果想知道具体哪个虚拟机下运行着单例对象是很困难的,而且单例类是很难实现序列化的。
由于端午节放假的缘故,各位同仁在期间提出了很多的问题,也指出了我思维的局限性,没有及时的更新于回复各位好友的,深表遗憾!
针对单例模式的很多用法,前面一直是在所单一线程的问题,本来设计多线程的问题就很少,也对双重锁定这个问题没有深入的五挖掘,导致了犯了今天这样的错误。
之后参考了一些资料,针对各位朋友提出的问题与我自身存在的问题,进行改进!
参考的文章是:IBMDeveloperWorks(中国)的网站上的文章
《
双重检查锁定及单例模式
》
(
http://www.ibm.com/developerworks/cn/java/j-dcl.html
)。这篇文章真针对各种问题都有分析,包括可见性等问题。得出了一个结论:双重锁定失效的主要原因是:不同JVM之间的无序写入问题,多线程之间的独占、休眠(记忆复苏)所引起的不同
不问题。
最终本文提出了一个建议:
建
议不要使用“双重锁定”!
一个解决单例模式的方案:
底线就是:无论以何
种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。JSR-133 是有关内存模型寻址问题的,尽管如此,新的内存模型也不会支持双重检查锁定。因此,您有两种选择:
-
接受如清单 2 中所示的
getInstance()
方法的同步。
-
放弃同步,而使用一个
static
字段。
选择项 2 如清单 10 中所示:
- /**
- *<span style="font-
family: verdana, nsimsun, sans-serif; white-space: normal; line-
height: 19px;"><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();
- private
Singleton()
- {
- v = new
Vector();
- inUse = true
;
- //...
- }
- public
static
Singleton getInstance()
- {
- return
instance;
- }
- }