今天看到一篇文章,上面这样提到:   
  “在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方法中定义表示客户状态的实例变量还要同步啊?什么是代表特定客户状态的实例变量呢?
       能将我问的问题解决,我定把分跟您!
      未能全部解决的,给部份分,呵呵!先谢了!

解决方案 »

  1.   

    Struts会为每一个请求创建一个Action类的实例
      

  2.   

    楼主对实例变量的理解还不是很清晰.其实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个连接时就会发生错误.   实例变量的安全性就在于,没有任何方式可以确保另一线程内的请求由于将其自身的引用保存到变量中而不打断该变量的连续性.  解决此问题,可以:
             在服务方法中定义局部变量.
      

  3.   

    楼主的actionForm是线程安全的,他只是作为一个局部变量传入。
      

  4.   

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

  5.   

    在Action类中定义的实例变量.是说这个Action类的属性字段
      

  6.   

    孙卫琴的<<基于mvc..>> p110 说的挺清楚!
      

  7.   

    "特别是execute方法中定义代表特定客户状态的实例变量。"
    上面这句话问题多多:    在execute定义的变量只能叫局部变量.
        局部变量不会被线程共享,肯定是安全的
      

  8.   

    大侠顺便帮我看看这个类会存在线程安全问题吗?我的EmployeeServices 是一个接口,它完成对数据库的操作,用spring 的HibernateTemplate类实现对数据库的操作。
    package com.yumeng.sshdemo.action;import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;import com.yumeng.sshdemo.form.EmployeeForm;
    import com.yumeng.sshdemo.po.Employee;
    import com.yumeng.sshdemo.service.EmployeeServices;
    public class DeleteAction extends Action {

    private EmployeeServices employeeServices;
    public EmployeeServices getEmployeeServices() {
    return employeeServices;
    }
    public void setEmployeeServices(EmployeeServices employeeServices) {
    this.employeeServices = employeeServices;
    }
    public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response) {
    EmployeeForm employeeForm = (EmployeeForm) form;
    Employee employee = new Employee();
    employee.setEmployee_id(employeeForm.getemployee_id());
    employeeServices.deleteEmployee(employee);
    return mapping.findForward("delete");
    }

    }
    在这里我定义了一个类级变量,这段程序会有线程安全问题吗?EmployeeServices在Spring配置文件中被配置为singleton=false,在并发访问的时候会出现混乱吗?
      

  9.   

    你给出的代码不会出现并发问题。“因此,必须保证在多线程环境中,Action也能正常工作。因此在Action类中要谨慎使用实例变量...”
    这个的意思是说下面这种:
    public class XXXAction extends Action {
        private String userName;    public ActionForward execute(...) {
           userName = request.getParameter(...);
           functionA();
        }
       
        private functionA() {
           xxx(userName);
        }
    }这个例子里,userName就是出问题的“实例变量”,由于Action只创建一个实例,那么多线程环境下,线程1运行到functionA()的时候,线程2可能就把userName给改掉了,所以导致线程1的运行结果出错。别笑哦,我真见过这么写的.....至于你给出的代码,类似DAO的模式,一般来说,即使是singleton=true,也不会有问题的(一般也应该用singleton)。
      

  10.   

    我们必须明白什么样的变量是线程安全的,方法内的局部变量和方法的参数变量都是线程安全的,所以即使系统只启用一个Action对象为所有线程服务,因为Form对象对每个请求来说都是单独创建的,而且是做为参数传到Action里,所以Form对象一定是线程安全的.
    而"因此不能在Action类,特别是execute方法中定义代表特定客户状态的实例变量。
     如果要采用实例变量,需要采用Java的线程同步机制.." 这句话我个人认为很值得推敲,建议楼主别再去花精力琢磨它了
      

  11.   

    employeeServices存在多线程的问题删掉get set 后面改成(new EmployeeServices()).deleteEmployee(employee);
      

  12.   

    肯定不会出现并发的问题.
    像你这样"删掉get set 后面改成(new EmployeeServices()).deleteEmployee(employee);"还要SPRING干什么呢.还哪来的IOC