因为项目需要,在开发过程中写了大量的工具类,而且为了调用方便,工具类中的方法全部是静态方法。今天,脑袋里灵光一闪,突然就想,如果在高并发的情况下,同时去调用工具类的静态方法,会不会导致严重的性能问题?
有了想法,就要付诸于行动,于是,本人本着精益求精的精神,写了几个测试类如下:
COMUT类是主类,负责生成大量的线程实例。
import java.util.Date;
/**
* @Title: COMUT.java
* @Copyright (C) 2015 <span style="font-family: Arial, Helvetica, sans-serif;">卓盐</span>
* @Description:
* @Revision History:
* @Revision 1.0 2015-5-19 卓盐
*/
/**
* @ClassName: COMUT
* @Description: Description of this class
* @author <a href="mailto:songfuhong@szlongtu.com">卓盐</a>于 2015-5-19 下午03:15:40
*/
public class COMUT {
public COMUT() {
}
public static void main(String[] args) {
int times = 5000;
int type = 1;
try {
Date setTime = new Date();
// 设定为启动程序后的2s执行
setTime.setTime(setTime.getTime() + 5000);
// 测试多线程情况下的性能问题
Thread[] thread = new Thread[times];
for (int i = 0; i < times; i++) {
thread[i] = new Thread(new ThreadTest("thread" + i, setTime, type));
}
for (int i = 0; i < times; i++) {
thread[i].start();
}
for (int i = 0; i < times; i++) {
thread[i].join();
}
// 等待所有子线程执行完毕后,输出下面的内容
System.out.println("本次测试方案:" + type + "。最快:" + CulculateUtil.getMin() + "ms。最慢:" + CulculateUtil.getMax()
+ "ms。平均:" + CulculateUtil.getSum() / CulculateUtil.getTimes() + "ms.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
ICUtils是工具类,其中的两个静态方法,一个是将15位身份证转化为18位,另一个是校验15位身份证号码的正确性。
/**
* @Title: IdentiFierChanslateUtils.java
* @Copyright (C) 2015 <span style="font-family: Arial, Helvetica, sans-serif;">卓盐</span>
* @Description:
* @Revision History:
* @Revision 1.0 2015-5-19 卓盐
*/
/**
* @ClassName: IdentiFierChanslateUtils
* @Description: Description of this class
* @author <a href="mailto:songfuhong@szlongtu.com">卓盐</a>于 2015-5-19 下午03:17:16
*/
public class ICUtils {
/**
* 修补15位居民身份证号码为18位
* @param personIDCode
* @return
*/
public static String fixPersonIDCode(String personIDCode)
{
if (personIDCode == null || personIDCode.trim().length() != 15) {
return personIDCode;
}
String id17 = personIDCode.substring(0, 6) + "19" + personIDCode.substring(6, 15); // 15为身份证补\'19\'
// char[] code =
// {\'1\',\'0\',\'X\',\'9\',\'8\',\'7\',\'6\',\'5\',\'4\',\'3\',\'2\'};
// //11个
char[] code = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' }; // 11个
int[] factor = {0, 2, 4, 8, 5, 10, 9, 7, 3, 6, 1, 2, 4, 8, 5, 10, 9, 7 }; // 18个;
int[] idcd = new int[18];
int i;
int j;
int sum;
int remainder;
for (i = 1; i < 18; i++)
{
j = 17 - i;
idcd[i] = Integer.parseInt(id17.substring(j, j + 1));
}
sum = 0;
for (i = 1; i < 18; i++)
{
sum = sum + idcd[i] * factor[i];
}
remainder = sum % 11;
String lastCheckBit = String.valueOf(code[remainder]);
return id17 + lastCheckBit;
}
/**
* 判断是否是有效的18位或15位居民身份证号码
* @param identityId :18位或15位居民身份证号码
* @return:true: 有效的18位或15位居民身份证号码
*/
public static boolean isIdentityId(String identityId) {
if (isEmpty(identityId))
return false;
try {
if (identityId.length() == 18) {
String identityId15 = identityId.substring(0, 6) + identityId.substring(8, 17);
// System.out.println("the identityId15 is : "+identityId15);
if (fixPersonIDCode(identityId15).equalsIgnoreCase(identityId)) {
return true;
} else {
return false;
}
} else if (identityId.length() == 15) {
try {
Long.parseLong(identityId);
return true;
} catch (Exception ex) {
return false;
}
} else {
return false;
}
} catch (Exception ex) {
return false;
}
}
/**
* 判断是否为空串""
*/
public static boolean isEmpty(String sValue) {
if (sValue == null)
return true;
return sValue.trim().equals("") ? true : false;
}
}
ICUtilsInstance中的内容和ICUtils中完全一致,只是其中的方法都是实例化方法。
/**
* @Title: IdentiFierChanslateUtils.java
* @Copyright (C) 2015 <span style="font-family: Arial, Helvetica, sans-serif;">卓盐</span>
* @Description:
* @Revision History:
* @Revision 1.0 2015-5-19 卓盐
*/
/**
* @ClassName: IdentiFierChanslateUtils
* @Description: Description of this class
* @author <a href="mailto:songfuhong@szlongtu.com">卓盐</a>于 2015-5-19 下午03:17:16
*/
public class ICUtilsInstance {
/**
* 修补15位居民身份证号码为18位
* @param personIDCode
* @return
*/
public String fixPersonIDCode(String personIDCode)
{
if (personIDCode == null || personIDCode.trim().length() != 15) {
return personIDCode;
}
String id17 = personIDCode.substring(0, 6) + "19" + personIDCode.substring(6, 15); // 15为身份证补\'19\'
// char[] code =
// {\'1\',\'0\',\'X\',\'9\',\'8\',\'7\',\'6\',\'5\',\'4\',\'3\',\'2\'};
// //11个
char[] code = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' }; // 11个
int[] factor = {0, 2, 4, 8, 5, 10, 9, 7, 3, 6, 1, 2, 4, 8, 5, 10, 9, 7 }; // 18个;
int[] idcd = new int[18];
int i;
int j;
int sum;
int remainder;
for (i = 1; i < 18; i++)
{
j = 17 - i;
idcd[i] = Integer.parseInt(id17.substring(j, j + 1));
}
sum = 0;
for (i = 1; i < 18; i++)
{
sum = sum + idcd[i] * factor[i];
}
remainder = sum % 11;
String lastCheckBit = String.valueOf(code[remainder]);
return id17 + lastCheckBit;
}
/**
* 判断是否是有效的18位或15位居民身份证号码
* @param identityId :18位或15位居民身份证号码
* @return:true: 有效的18位或15位居民身份证号码
*/
public boolean isIdentityId(String identityId) {
if (isEmpty(identityId))
return false;
try {
if (identityId.length() == 18) {
String identityId15 = identityId.substring(0, 6) + identityId.substring(8, 17);
// System.out.println("the identityId15 is : "+identityId15);
if (fixPersonIDCode(identityId15).equalsIgnoreCase(identityId)) {
return true;
} else {
return false;
}
} else if (identityId.length() == 15) {
try {
Long.parseLong(identityId);
return true;
} catch (Exception ex) {
return false;
}
} else {
return false;
}
} catch (Exception ex) {
return false;
}
}
/**
* 判断是否为空串""
*/
public boolean isEmpty(String sValue) {
if (sValue == null)
return true;
return sValue.trim().equals("") ? true : false;
}
}
最后一个类ThreadTest,是线程类,构造函数需传入两个参数,一个是线程名,一个是设定的时间。线程主体run方法中,while循环负责校验当前时间是否已到设定时间。到了设定时间就去调用工具类中的两个方法。并将调用的时间记录下来,并输出。
/**
* @Title: ThreadTest.java
* @Copyright (C) 2015 <span style="font-family: Arial, Helvetica, sans-serif;">卓盐</span>
* @Description:
* @Revision History:
* @Revision 1.0 2015-5-19 卓盐
*/
/**
* @ClassName: ThreadTest
* @Description: Description of this class
* @author <a href="mailto:songfuhong@szlongtu.com">卓盐</a>于 2015-5-19 下午03:43:45
*/
public class ThreadTest implements Runnable {
/**
* @Fields threadName : Description
*/
private String threadName;
/**
* @Fields setTime : Description
*/
private Date setTime;
/**
* @Fields ic : Description
*/
ICUtilsInstance ic;
/**
* @Fields type : Description
*/
int type;
/**
* .
* <p>
* Title: run
* </p>
* <p>
* Description:
* </p>
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
while (true) {
Date d = new Date();
if (d.after(setTime))
break;
}
Date d1 = new Date();
if (type == 1) {
ICUtils.fixPersonIDCode("650103760113073");
ICUtils.isIdentityId("650103760113073");
} else if (type == 2) {
ic = new ICUtilsInstance();
ic.fixPersonIDCode("650103760113073");
ic.isIdentityId("650103760113073");
} else if (type == 3) {
ic.fixPersonIDCode("650103760113073");
ic.isIdentityId("650103760113073");
}
Date d2 = new Date();
long dif = d2.getTime() - d1.getTime();
System.out.println("线程名为:" + threadName + "的线程,运行时间为:" + dif + "ms.");
CulculateUtil.finish(dif);
}
/**
* 创建一个新的实例 ThreadTest.
* @param threadName
*/
public ThreadTest(String threadName, Date setTime, int type) {
super();
this.threadName = threadName;
this.setTime = setTime;
this.type = type;
if (type == 3) {
ic = new ICUtilsInstance();
}
}
}
还有一个类,用来统计分析用。
/**
* @Title: CulculateUtil.java
* @Copyright (C) 2015 <span style="font-family: Arial, Helvetica, sans-serif;">卓盐</span>
* @Description:
* @Revision History:
* @Revision 1.0 2015-5-19 卓盐
*/
/**
* @ClassName: CulculateUtil
* @Description: Description of this class
* @author <a href="mailto:songfuhong@szlongtu.com">卓盐</a>于 2015-5-19 下午04:10:06
*/
public class CulculateUtil {
/**
* @Fields sum : Description
*/
private static long sum = 0;
/**
* @Fields max : Description
*/
private static long max = 0;
/**
* @Fields min : Description
*/
private static long min = 999999999;
/**
* @Fields times : 调用次数
*/
private static int times = 0;
/**
* 某个线程结束时需要回掉的方法.
* @param dif 花费时间
* @throws
*/
public static void finish(long dif) {
times++;
if (dif < min) {
min = dif;
}
if (dif > max) {
max = dif;
}
sum += dif;
}
/**
* @return max
*/
public static long getMax() {
return max;
}
/**
* @param max 要设置的 max
*/
public static void setMax(long max) {
CulculateUtil.max = max;
}
/**
* @return min
*/
public static long getMin() {
return min;
}
/**
* @param min 要设置的 min
*/
public static void setMin(long min) {
CulculateUtil.min = min;
}
/**
* @return times
*/
public static int getTimes() {
return times;
}
/**
* @param times 要设置的 times
*/
public static void setTimes(int times) {
CulculateUtil.times = times;
}
/**
* @return sum
*/
public static long getSum() {
return sum;
}
/**
* @param sum 要设置的 sum
*/
public static void setSum(long sum) {
CulculateUtil.sum = sum;
}
}
本人在测试时,分别用100、1000和5000个线程进行了9次测试。在不同线程数情况下,分别调用了静态方法,在方法体中实例化并调用实例方法,及在构造方法中实例化并调用实例方法,测试结果如下:
线程数 | 静态方法 | 方法中实例化 | 构造方法中实例化 | ||||||
最快 | 平均 | 最慢 | 最快 | 平均 | 最慢 | 最快 | 平均 | 最慢 | |
100 | 1+0+0=1 | 7+5+3=25 | 15+16+11=42 | 2+0+0=2 | 6+4+9=19 | 13+14+17=44 | 0+0+0=0 | 1+1+0=2 | 13+15+5=33 |
1000 | 0+0+0=0 | 24+11+26=61 | 68+63+109=240 | 0+0+0=0 | 3+22+2=27 | 65+68+66=199 | 0+0+0=0 | 24+14+26=64 | 52+47+65=164 |
5000 | 0+0+0=0 | 7+17+76=100 | 94+162+310=566 | 0+0+0=0 | 12+15+7=34 | 267+114+102=483 | 0+0+0=0 | 0+0+4=4 | 55+78+81=214 |
总和 | 1 | 186 | 848 | 2 | 80 | 726 | 0 | 70 | 411 |
总结:在并发数较少的情况下,在构造方法中实例化并在方法中调用略占优,但三者性能无太大差别。当并发数较多时,尤其大并发情况下,优先推荐构造方法中实例化,再其次是方法中实例化,最后再是静态方法。高并发情况下,静态方法对性能影响比较大。
附件中上传源码及excel格式的测试结果,供大家参考。