`
wangyanlong0107
  • 浏览: 481252 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

【转】Singleton和Double-Checked Locking设计模式—UML图及代码实现

 
阅读更多

Singleton和Double-Checked Locking设计模式,分别指的是单例模式和双重检查锁模式,它们都可以用于确保某个类只有一个对象实例化。

 

两个模式的区别在于:Singleton模式用在单线程应用程序中,而Double-Checked Locking模式用于多线程模式。

 

一、Singleton模式

 

UML图:

 

 

代码:

 

package bupt.xujinliang.singletonpattern;
/**
 * 
 * @author jin
 *
 */
public class SingletonExample {
	public static void main(String[] args) {
		Printer printer1 = Printer.getInstance();
		Printer printer2 = Printer.getInstance();
		if(printer1 == printer2) {
			System.out.println("printer2 point to the same address with printer1");
		} else {
			System.out.println("printer2 point to different address with printer1");
		}
	}
}
class Printer {
	private static Printer instance;
	public Printer() {
		System.out.println("Printer Constructor");
	}	
	public static Printer getInstance() {
		if(null == instance) 
			instance = new Printer();
		return instance;
	}
}


运行结果:

 

2.Double-Checked Locking模式

 

Double Check Locking模式是singleton的多线程版本,必须使用锁来锁定临界区,当多个线程存在访问临界区的意图时,保证了临界区只被访问一次。

 

首先介绍其在C/C++环境下的实现过程:

 

代码1:

 

Printer* get_instance(void)
{
    lock();
    if( instance == 0) {
       instance = new Printer;
    }
    unlock();
    return instance;
}
上述代码存在的问题是:无论是否已经初始化都要加锁,增加了负荷,已经没有所谓的并发性能了。

 

代码2:

 

Printer* get_instance(void)
{ 
    if( instance == 0){
        lock();
        instance = new Printer;
        unlock();
    }
    return instance;
}
上述代码存在的问题是:不能保证临界区只初始化一次,没能实现singleton的基本功能。

 

代码3:

 

Printer* get_instance(void)
{ 
    if( instance == 0){
        lock();
        if( instance == 0 )
            instance = new Printer;
        unlock();
    }
    return instance;
}
这是比较完善的Double-Checked Locking模式实现的代码。

 

为什么叫做Double-Checked Locking呢?请看上述代码3,可以看到在加锁前后都对instance变量进行了检查,故谓之Double-Checked Locking。

 

那么在Java中的实现与在C/C++中不同吗?是的。

 

下面的的Java代码是不能够实现Double-Checked Locking模式的:

 

class Printer {
	private static Printer resource ;
	public static Printer getInstance(){	
		if(resource == null ){
			synchronized (DoubleCheckedLockingExample.class) {
				if(resource  == null ){
					resource  = new Printer() ;
				}
			}			
		}		
		return resource ;
	}	
	private Printer(){}
}
上面程序真正的问题是没有同步的情况下读取共享变量resource,并发的情况下对象的状态值有可能是过期无效的。要解决这个问题也很简单,把resource声明为volatile类型。volatile有什么作用?引用《java并发编程实战》的解析:volatile在引用是最新,但对象状态是陈旧的状况下可以起到作用,保证都是最新。

 

当一个域声明为volatile类型后,编译器与运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。volatile变量不会缓存在寄存器或缓存在对其他处理器隐藏的地方。所以,读一个volatile类型的变量时,总会返回由某一线程所写入的最新值。
读取volatile变量比读取非volatile变量的性能几乎没有差别,不过需要注意的是volatile只能保证内存可见性,并不能保证原子性。

 

现给出Java在多线程下实现单个实例化对象的方法:

 

class Printer {
	private static class Instance {
		static final Printer instance = new Printer();
	}
	private static Printer resource ;
	public static Printer getInstance(){		
		return Instance.instance;
	}	
	private Printer(){}
}

上述方法之所以有效,是因为内部类(Instance)将只被装载一次,所以只会创建一个对象。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics