Java Web-Servlet线程问题
什么情况下会存在线程安全问题?
条件1:多线程并发
条件2:有共享数据
条件3:共享数据涉及到修改操作
怎么解决线程安全问题?
选择1:使用局部变量
选择2:将单例改为多例
选择3:使用线程同步机制synchronized
JVM包括三块主要的内存空间:栈内存、堆内存(共享数据)、方法区内存(共享数据)
Java单例模式是确bai保某个类只有一个实例,du而且自行实例化并向整zhi个系统提供这个实例,在计dao算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例的模式;
Java单例模式分三种:懒汉式单例、饿汉式单例、登记式单例。
(1)Java单例模式有以下特点:单例类只能有一个实例;单例类必须自己创建自己的唯一实例;单例类必须给所有其他对象提供这一实例。
(2)Java单例模式的应用范围:每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中,每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
Servlet的多线程机制
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行,
这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。
public class ConcurrentTest extends HttpServlet {
PrintWriter output;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username;
response.setContentType("text/html;charset=gb2312");
username=request.getParameter("username");
output=response.getWriter();
try {
Thread.sleep(5000); //为了突出并发问题,在这设置一个延时
output.println("用户名:"+username+"<BR>");
} catch (Exception e) {
e.printStackTrace();
}
}
}
该Servlet中定义了一个实例变量output,在service方法将其赋值为用户的输出。当一个用户访问该Servlet时,程序会正常的运行,但当多个用户并发访问时,就可能会出现其它用户的信息显示在另外一些用户的浏览器上的问题。这是一个严重的问题。为了突出并发问题,便于测试、观察,我们在回显用户信息时执行了一个延时的操作。假设已在web.xml配置文件中注册了该Servlet,现有两个用户a和b同时访问该Servlet(可以启动两个IE浏览器,或者在两台机器上同时访问),即同时在浏览器中输入:
1、实现 SingleThreadModel 接口
该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。这种方法只要将前面的Concurrent Test类的类头定义更改为
public class ConcurrentTest extends HttpServlet implements SingleThreadModel
2、同步对共享数据的操作
使用synchronized 关键字能保证一次只有一个线程可以访问被保护的区段,在本论文中的Servlet可以通过同步块操作来保证线程的安全。同步后的代码如下:
public class ConcurrentTest extends HttpServlet {
PrintWriter output;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username;
response.setContentType("text/html;charset=gb2312");
username=request.getParameter("username");
synchronized(this){
output=response.getWriter();
try {
Thread.sleep(5000);//为了突出并发问题,在这设置一个延时
output.println("用户名:"+username+"<BR>");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
本文地址:https://blog.csdn.net/Longsoul/article/details/107598145