刚好最近在研究白盒测试静态扫描,于是就开发了findbugs的插件MULTITHREAD_SINGLETON,该插件能够扫描出所有的单例类,并且判断该单例类是否存在引发线程安全的属性。现在已经应用到项目中扫描开发的类,效果还不错。
下面介绍该插件的设计思路。
1.1 MULTITHREAD_SINGLETON
多线程在单例情况引发线程安全问题
1.1.1 作者
Author: River.liu
Date : 2010.3.30
Email : liuhanhong@yahoo.com.cn
1.1.2 原理
1、单例类如果存在属性,如果属性是: 基本类型 非final;非基本类型 非static..
那么可能会在该单例类中的其他方法里面写该属性,在多线程情况
下会出现读写混乱。
注意struts1.x的action也是单例来的。
2、例如
public class SingletonA {
private StringBuffer vo = new StringBuffer();
private SingletonA(){
}
private static SingletonA a=null;
public static SingletonA getInstance(){
if(a==null){
a=new SingletonA();
}
return a;
}
public void setVo(StringBuffer invo){
vo=invo;
}
public StringBuffer getVo(){
return this.vo;
}
}
当在多线程调用该代码的情况下就会出现问题如:
SingletonA s=SingletonA.getInstance();
s.setVo(new StringBuffer(Thread.currentThread().getName()));
//这里取到的值,可能是别的线程写上去的
StringBuffer a=s.getVo();
1.1.3 开发原理
1、判断是否为单例类
>>如果该类为配置文件中MULTITHREAD_SINGLETON.parentClassSingleton指定
的子类,那么一定就是单例
>>如果配置文件中没指定
那么就判断是否存在公开的静态方法,并且该方法返回的值是类本身
2、判断是否存在安全隐患的属性
如果是单例。如果存在属性:基本类型非final;非基本类型非static的属性,那么
该属性的读写就存在安全隐患。
基本类型目前定义为:
boolean、int、short、byte、long、double、float、char、void、java.lang.String
例如:
基本类型
//安全,基本、final,因为为基本类型,永远都不会改变
final int a=0;
//安全,因为String是不可变对象
final String sb=”ddd”;
//不安全,基本、非final
String sb=”ddd”;
非基本类型
//不安全.非基本、非static
StringBuffer sb=new StringBuffer();
//不安全,虽然sb虽然不变,但是仅仅是对象的句柄不变,对象里面的内容是可以变的,
//是可以调用append等方法改变里面的值
final StringBuffer sb=new StringBuffer();
//安全,非基本、static,该情况不做安全检查
static StringBuffer sb=new StringBuffer();
1.1.4 配置说明
配置文件pluginConfig.properties在插件的jar包里面,直接修改里面的配置项目,再放回jar包就可以了。
1.1.4.1 parentClassSingleton
参数值,表示扩展该类的所有类都是单例。
多个参数值,以逗号隔开。
由于实际开发中,有不少的类虽然没有按照常规定义单例那样来定义类,但是可能会采用用缓存等机制来保证是单例。所以我们可以根据需要来自定义去设定某些类的子类都是单例。比如stuts1.X的所有继承Action类都是单例来的。
例如:
MULTITHREAD_SINGLETON.parentClassSingleton=demo.Second,org.apache.struts.action.Action
表示凡是扩展demo.Second,org.apache.struts.action.Action类的都是单例