使用纯Jdbc设计数据库连接池的时候,设计了数据库连接类,其对象是可重用的,所有的使用者依靠状态标记来获取该对象:
每一个使用者使用前要判断状态,如果可用的话,设置成占用状态,然后开始使用,用完恢复成可用状态。
这就需要每一个使用者用完后要恢复状态,如果没有恢复状态,该对象就无法再被使用(这个过程当然是要同步的)。
由于恢复状态需要coder调用,coder可能会遗忘而没有恢复状态(这个遗忘有可能是确确实实的遗忘,也有可能是编码不当,比如没有考虑到的异常),这会造成资源的浪费。
这个错误如果频繁的话,对于DB Connection来说是一件非常恐怖的事情。
为了处理遗忘而没有恢复状态的,可以使用超时机制,使用thread来进行定时扫描,检测超时使用的对象(超时有可能是没有恢复状态,也有可能确实是运行需要更长的时间,如果不重要的话而且不是经常发生的话,可以简单的“宁枉勿纵” ~_~)。
如果超时的原因确实是没有恢复状态,为了改正这个错误,只靠超时机制是解决不了这个问题的,需要定位到其调用上。
改正可以依赖于人工的查看代码。但这有很大弊端,对于大量的调用要耗费大量的人工,而且如果这个错误比较隐蔽,有可能人工查看都未必能找出来。
另一种就是我们可以自动的进行定位,如果知道调用堆栈,就知道了特定方法的调用
这就是本文的目的--取得Java的调用堆栈。
谈到Java的调用堆栈,很自然的想到经典一行程序 e.printStackTrace()
Exception也提供把异常信息输出到给定的PrintWriter对象或者PrintStream对象中
void printStackTrace(PrintWriter s)
void printStackTrace(PrintStream s)
因此我们可以使用如下的方法取得调用堆栈的信息
StringWriter sw=new StringWriter();
Exception e=new Exception("Debug Mode");
e.printStackTrace(new PrintWriter(sw));
但这个结果还是需要进行进一步处理的,查看printStackTrace的源代码,原来printStackTrace输出是对getOurStackTrace()返回的StackTraceElement[]进行遍历然后输出的结果,到这里找到了我们解决我们问题的方法
Throwable提供 public StackTraceElement[] getStackTrace() 返回当前调用堆栈,函数里面就一句
return (StackTraceElement[]) getOurStackTrace().clone();
至此我们可以调用Throwable对象的getStackTrace()方法得到堆栈,然后自己处理。
下面的代码就足够了
Throwable throwable=new Throwable();
StackTraceElement[] elements=throwable.getStackTrace();
很明显,这里的获取调用堆栈,只是为了为了监视调用的,对正常的业务来说完全是多余的,而且返回对象的clone,都是无益于业务的,因此,为了解决我的问题,设置Debug标记标识当前处于Release Mode还是Debug Mode,只在Debug Mode才处理堆栈信息
实例如下:
public class CallStackUtil
{
private static boolean debugMode=false;
public static boolean isDebugMode()
{
return debugMode;
}
public static void setDebugMode(boolean debugMode)
{
CallStackUtil.debugMode = debugMode;
}
public static StackTraceElement[] getCurrentCallStack()
{
if(debugMode)
System.out.println("Debug Mode");
else
System.out.println("Normal Mode");
if(debugMode)
{
Throwable throwable=new Throwable();
return throwable.getStackTrace();
}
return null;
}
}
public class test
{
public static void main(String[] para)
{
CallStackUtil.setDebugMode(true);
caller();
}
public static void methodMonitored()//被监控的方法
{
StackTraceElement[] elements=CallStackUtil.getCurrentCallStack();
if(elements!=null)
if(elements.length>2)
System.out.println(elements[2].toString().split("//(")[0]+"() calling "+elements[1].toString().split("//(")[0]+"()!");
}
public static void caller()
{
methodMonitored();
}
}
运行输出结果:
Debug Mode
test.caller() calling test.methodMonitored()!
在Debug Mode下,数据库连接类只需记录特定函数的最后一次调用者,从而在某个对象占用超时的时候使用该信息。
其实这种方法也可以限制某方法的调用者只能为给定的类或其方法等,这可以实现C++里面类似友元函数的功能。