从测试的角度去看单列模式

2011-03-16  蔡秋亮 

前段时间在做接口测试的时候,发现开发的代码中会用到很多Java设计模式,比如工厂模式、单列模式以及观察者模式等等。在测试的过程我往往会先大体看一下开发写的代码,有时在code review的过程就能发现代码中存在一些bug,在这里主要将我近期发现的一个单列模式的bug以及对单列模式做的一些调研和大家分享一下。

单列模式主要是保证在Java应用程序中,一个class只有一个实例对象存在。
在很多操作中,比如建立目录,数据库连接都需要这样的单线程操作。Singleton模式就为我们提供了这样实现的可能。使用singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收。
一般Singleton模式通常有几种形式:
1. public class Singleton{
private Singleton(){}// 注意构筑函数必须是private的
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}

2.private static Singleton instance = null;
  public static synchronized Singleton getInstance() {
  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
  //使用时生成实例,提高了效率!
  if (instance==null)
    instance=new Singleton();
  return instance;   
}

上面第二种形式是Lazy initialization,也就是说第一次调用时使用初始的Singleton,以后就不用再生成了。
注意到第二种形式中synchronized,这个synchronized非常重要,如果没有synchronized,那么使用getInstance()是有可能得到多个实例的。我们在这里主要也就是讨论第二种形式,因为我的bug也就是在第二种形式的单列模式中找到的。

首先我们来看以下这段最普通的单列模式代码:
list1:
public class Singleton
{
private static Singleton instance;
private Vector v;
private boolean inUse;

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

public static Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
}
我们看到上面这段代码,在getInstance()方法前面没有synchronized,那么在多线程的情况下我们来看看程序的运行:
1. Thread1 调用getInstance()方法的时候发现instance是null在line1处.
2. Thread1 开始进入if模块,但是在执行line2的时候Thread2抢占了Thread1的cpu执行时间。
3. Thread2 开始调用getInstance()方法并且发现instance是null,因此Thread2进入if模块并创建了一个新的Singleton实例。
4. 在line3,Thread2将创建的instance返回。
5. Thread2的cpu执行权限被Thread1抢占,这个时候Thread1开始执行line2并又创建了一个Singleton实例,并返回了该实例。
这个时候大家就会发现getInstance()方法被调用了两次,并创建了两个实例,bug就因此产生了,单列模式创建了两个实例。

那么这个时候我们的developer会给getInstance()前面加上synchronized。如list2的代码:
public static synchronized Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
这个时候我们发现一起都ok了,list2的代码在多线程调用的情况下已经可以很好的工作了,但是我们会发现一个问题,在多线程调用的情况下,我们只需要在第一次调用getInstance()的时候使用synchronized,因为只有在第一次调用的时候才会产生一个Singleton的实例,后续的调用其实都不需要synchronized,因为第一次调用getInstance()后,后续的线程都发现instance!=null并返回instance。因为我们加了sychronized,那么在多线程的情况,每一次调用getInstance()方法的时候都会做sychronization,这样势必会影响程序的执行效率,设想如果是一个web项目的话,那么就会出现web性能瓶颈,那么我们应该如何去提高这段代码的性能呢,大家可以做思考。

386°/3838 人阅读/3 条评论 发表评论

熊志男  2011-03-16

不知楼主的接口测试具体如何做的?请教


蔡秋亮  2011-03-17

熊志男: 不知楼主的接口测试具体如何做的?请教
如有兴趣可以加我msn:vitorcai@hotmail.com,我们可以探讨一下


熊志男  2011-03-17

蔡秋亮: 如有兴趣可以加我msn:vitorcai@hotmail.com,我们可以探讨一下
加了:)


登录 后发表评论