日期:2014-05-18  浏览次数:20755 次

一个关于Action线程安全的问题,大侠帮我看看,定给分!
今天看到一篇文章,上面这样提到:      
    “在Struts的生命周期中,只会为每个Action类创建一个实例,所有的客户请求共享这个实例。因此,必须保证在多线程环境中,Action也能正常工作。因此在Action类中要谨慎使用实例变量...      
            如果在Action类中定义了实例变量,那么在Action实例的整个生命周期中,这个实例变量被所有请求的线程共享。因此不能在Action类,特别是execute方法中定义代表特定客户状态的实例变量。如果要采用实例变量,需要采用Java的线程同步机制..”
            我有点不太明白:      
            通常一个ActionForm对象作为Action类中execute()方法的参数传入。并且我们可能会调用相应的get,set方法来对这个ActionForm对象中存储的用户提交的表单中信息进行操作。甚至将这个ActionForm对象转发给其他处理业务逻辑的java类。   如果按照上面文章中提到的在Action实例中定义的实例变量会被所有调用Action实例的线程所共享。那么通常有n个不同的用户因为同时递交相同的表单而使用这个Action实例(将各自request/session内的ActionForm实例作为参数传递给这个Action的实例),会不会相互间产生影响呢?这里java虚拟机是怎样处理各自传递来的对象的呢?      
            请哪位大侠能帮我指点一下,非常非常地感谢您!
            还有这句不懂:
"因此不能在Action类,特别是execute方法中定义代表特定客户状态的实例变量。
  如果要采用实例变量,需要采用Java的线程同步机制.. "
  不是说在方法中定义的变量是线程安全的吗,为什么在execute方法中定义表示客户状态的实例变量还要同步啊?什么是代表特定客户状态的实例变量呢?
              能将我问的问题解决,我定把分跟您!
            未能全部解决的,给部份分,呵呵!先谢了!




------解决方案--------------------
Struts会为每一个请求创建一个Action类的实例
------解决方案--------------------
这我知道啊,你没看清楚我提的问题啊!
------解决方案--------------------
用动态的ActionValidatorForm吧
------解决方案--------------------
呵呵,那么巧啊,我今天下午也是看到这个内容,孙卫琴写的,我也跟楼主有同样的疑问啊
------解决方案--------------------
楼主对实例变量的理解还不是很清晰.其实Action的线程安全问题,也是Servlet的线程安全问题.
因为实例变量是被多线程共享的,所以是不安全的.
举例如下:
package net.oicp.sunflowerbbs;
import java.io.*;
import java.sql.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class SomeServlet extends HttpServlet
{
private Connection con;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,Exception{
try
{
Class.forName( "someDriver ");
con=DriverManager.getConnection( "url ", "user ", "password ");
//下面是一系列数据库操作....
//...
}
catch(Exception e)
{
e.printStackTrace();
}
}
}

考虑当两个请求同时到达,只差隔几百毫秒.这时会发生什么,第一个请求打开数据库连接,保存其引用至con实例变量,然后他执行一个数据库操作.同时第二个请求到达,打开另一连接,在同一con实例变量中保存其引用.如果第一个操作完成,试图做另一个数据库操作,其不再拥有初始连接对象---他只知道第二个,然后试图使用第2个连接时就会发生错误.

实例变量的安全性就在于,没有任何方式可以确保另一线程内的请求由于将其自身的引用保存到变量中而不打断该变量的连续性.

解决此问题,可以:
在服务方法中定义局部变量.
------解决方案--------------------
如果在action的execute()方法中定义了局部变量,对于每个调用execute()方法的线程,java虚拟机会在每个线程的堆栈中创建局部变量,因此每个线程拥有 独 立 的 局部变量,不会被其他线程共享,当线程执行完execute()方法时,他的局部变量也会被销毁.
如果在action类中定义了实例变量,那么在整个action实例的生命周期中,这个实例变量是被所有请求线程共享.

看清楚
------解决方案--------------------
你给出的代码不会出现并发问题。

“因此,必须保证在多线程环境中,Action也能正常工作。因此在Action类中要谨慎使用实例变量...”
这个的意思是说下面这种:
public class XXXAction extends Action {
private String userName;

public ActionForward execute(...) {
userName = request.getParameter(...);