你真的会写线程安全的servlet吗?
很多人认为servlet随便怎样写可以的,反正是线程安全的,没有什么关系的.那我们来看看下面的这一个例子吧.
首先要看一下小段的servlet代码,如下:
public class Test extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req,
HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
我相信不少人的代码在经意或不经意间都这样写过,然后自己一个跑一下程序,可以啦,然后就不再理会啦,上面的程序是有问题的,假如有两个用户同时用到这一段代码的程序的功能,就有可能会出现如下这种情况:
引用
Thread 1: assign "A" to name
Thread 2: assign "B" to name
Thread 1: print "B, thanks for visiting!"
Thread 2: print "B, thanks for visiting!"
怎样改写呢?其实也比较简单的,比较出名的找bug的工具Fortify推荐如下一种方式如下:
public class Test extends HttpServlet {
protected void doPost (HttpServletRequest req, HttpServletResponse res) {
RequestHandler handler = new RequestHandler();
handler.handle(req, res);
}
}
public class RequestHandler {
String name;
public void handle(HttpServletRequest req, HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}
请记得这一句话:
引用
Do not use Servlet member fields for anything but constants. (i.e. make all member fields static final).
刚才今天我的一个同事也写了类似上面的有bug的那一个servlet程序,我也改造过来了;
不过是用了另一种方式实现的,用的是ThreadLocal这一个关键类,伪代码如下:
public class Test extends HttpServlet {
private static final ThreadLocal parameterLocal = new ThreadLocal();
private static final ThreadLocal filesLocal = new ThreadLocal();
protected Map getFiles() {
Map files = (HashMap)filesLocal.get();
if(files==null){
files = new HashMap();
filesLocal.set(files);
}
return files;
}
protected Map getParameters() {
Map parameters = (HashMap)parameterLocal.get();
if(parameters==null){
parameters = new HashMap();
parameterLocal.set(parameters);
}
return parameters;
}
public void doPost(HttpServletRequest request, HttpServletResponse response) {
.....//可以通过getParameters()返回Map对象
log.info(getParameters().get("possess"));
Iterator iterator = getFiles().values().iterator(); //可以通过getFiles()返回Map对象
FileItem item = (FileItem) iterator.next();
.....
}
}