转自:http://www.dev26.com/blog/article/372
在平时开发servlet的时候一直没考虑过线程安全方面的问题,尤其是在servlet变量共享和初始化方式等。
1,变量的线程安全:这里的变量指字段和共享数据(如表单参数值)。
a,将 参数变量 本地化。多线程并不共享局部变量.所以我们要尽可能的在servlet中使用局部变量。
例如:
b,使用同步块Synchronized,防止可能异步调用的代码块。这意味着线程需要排队处理。
在使用同板块的时候要尽可能的缩小同步代码的范围,不要直接在sevice方法和响应方法上使用同步,这样会严重影响性能。
2,属性的线程安全:ServletContext,HttpSession,ServletRequest对象中属性
ServletContext:(线程是不安全的)
ServletContext是可以多线程同时读/写属性的,线程是不安全的。要对属性的读写进行同步处理或者进行深度Clone()。
所以在Servlet上下文中尽可能少量保存会被修改(写)的数据,可以采取其他方式在多个Servlet中共享,比方我们可以使用单例模式来处理共享数据。
HttpSession:(线程是不安全的)
HttpSession对象在用户会话期间存在,只能在处理属于同一个Session的请求的线程中被访问,因此Session对象的属性访问理论上是线程安全的。
当用户打开多个同属于一个进程的浏览器窗口,在这些窗口的访问属于同一个Session,会出现多次请求,需要多个工作线程来处理请求,可能造成同时多线程读写属性。
这时我们需要对属性的读写进行同步处理:使用同步块Synchronized和使用读/写器来解决。
ServletRequest:(线程是安全的)
对于每一个请求,由一个工作线程来执行,都会创建有一个新的ServletRequest对象,所以ServletRequest对象只能在一个线程中被访问。ServletRequest是线程安全的。
注意:ServletRequest对象在service方法的范围内是有效的,不要试图在service方法结束后仍然保存请求对象的引用。
3,使用同步的集合类:
使用Vector代替ArrayList,使用Hashtable代替HashMap。
4,不要在Servlet中创建自己的线程来完成某个功能。
Servlet本身就是多线程的,在Servlet中再创建线程,将导致执行情况复杂化,出现多线程安全问题。
5,在多个servlet中对外部对象(比方文件)进行修改操作一定要加锁,做到互斥的访问。
6,javax.servlet.SingleThreadModel接口是一个标识接口,如果一个Servlet实现了这个接口,那Servlet容器将保证在一个时刻仅有一个线程可以在给定的servlet实例的service方法中执行。将其他所有请求进行排队。
服务器可以使用多个实例来处理请求,代替单个实例的请求排队带来的效益问题。服务器创建一个Servlet类的多个Servlet实例组成的实例池,对于每个请求分配Servlet实例进行响应处理,之后放回到实例池中等待下此请求。这样就造成并发访问的问题。
此时,局部变量(字段)也是安全的,但对于全局变量和共享数据是不安全的,需要进行同步处理。而对于这样多实例的情况SingleThreadModel接口并不能解决并发访问问题。
如果你用移动的变态网址来测试wml,仍然可能失败,比如:
浏览器打开http://211.136.165.53/tt.jsp [^] 密码:cmv2oo9
你会得到如下错误:
不要紧,这个是这个网站的dtd不规范的原因,移动把网页第一行的dtd写成了小写,所以,你可以去查找Invalid DTD Public ID出现的位置。经过查找,发现是在:
external/webkit/webcore/dom/XMLTokenizerLibxml2.cpp下面的internalSubset()方法中的:
#if ENABLE(WML)
if (isWMLDocument()
&& extId != "-//WAPFORUM//DTD WML 1.3//EN"
&& extId != "-//WAPFORUM//DTD WML 1.2//EN"
&& extId != "-//WAPFORUM//DTD WML 1.1//EN"
&& extId != "-//WAPFORUM//DTD WML 1.0//EN")
handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber());
#endif
这里按照规范只处理了大写,因此我们应该把出过来的extId转成大写:
#if ENABLE(WML)
extId = extId.upper();
if (isWMLDocument()
&& extId != "-//WAPFORUM//DTD WML 1.3//EN"
&& extId != "-//WAPFORUM//DTD WML 1.2//EN"
&& extId != "-//WAPFORUM//DTD WML 1.1//EN"
&& extId != "-//WAPFORUM//DTD WML 1.0//EN")
handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber());
#endif
这样就ok了。
注意:如果编译的时候出现
external/webkit/WebCore/page/Console.cpp: In member function 'WebCore::String WebCore::Console::lastWMLErrorMessage() const':
external/webkit/WebCore/page/Console.cpp:296: error: 'class WebCore::Page' has no member named 'inspectorController'
时,需要去更改external/webkit/webcore/page/Console.cpp中lastWMLErrorMessage()方法:
#if ENABLE(WML)
String Console::lastWMLErrorMessage() const
{
Page* page = this->page();
if (!page)
return String();
// const Vector<ConsoleMessage*>& consoleMessages = page->inspectorController()->consoleMessages();
// if (consoleMessages.isEmpty())
// return String();
// Vector<ConsoleMessage*>::const_iterator it = consoleMessages.begin();
// const Vector<ConsoleMessage*>::const_iterator end = consoleMessages.end();
// for (; it != end; ++it) {
// ConsoleMessage* message = *it;
// if (message->source() != WMLMessageSource)
// continue;
// return message->message();
// }
return String();
}
#endif
webkit jni 层在extenal/webkit/webkit/android 目录
参考:
http://blog.csdn.net/hxwwf/article/details/7344280
http://seya.iteye.com/blog/931289
http://blog.csdn.net/weiyouyin/article/details/5739659
http://blog.csdn.net/perfectpdl/article/details/7073786
[教程指导] 为菜鸟扫盲 名词解释三码 五码 改局端 EM等等
双环上网教程
[求助] 查好的五码写号后被改局端就会作废???
备忘