当前位置: 首页 > news >正文

Struts的线程安全问题

本文系转载,转自:http://blog.csdn.net/xiemk2005/article/details/6064435

一、Servlet的线程安全问题

Servlet是单例的,如果Servlet有定义实例变量并且在service方法中有赋值操作,则在多线程情况下存在线程安全问题。

二、Struts1

Struts1使用的ActionServlet是单例的,由这一个servlet处理所有.do请求。RequestProcessor也是单例。

RequestProcessor的processActionCreate方法:

    /** 
      * <p>Return an <code>Action</code> instance that will be used to process 
      * the current request, creating a new one if necessary.</p> 
      * 
      * @param request  The servlet request we are processing 
      * @param response The servlet response we are creating 
      * @param mapping  The mapping we are using 
      * @return An <code>Action</code> instance that will be used to process 
      *         the current request. 
      * @throws IOException if an input/output error occurs 
      */  
     protected Action processActionCreate(HttpServletRequest request,  
         HttpServletResponse response, ActionMapping mapping)  
         throws IOException {  
         // Acquire the Action instance we will be using (if there is one)  
         String className = mapping.getType();  
         if (log.isDebugEnabled()) {  
             log.debug(" Looking for Action instance for class " + className);  
         }  
         Action instance;  
         // 这个同步快保证了Action的单例  
         synchronized (actions) {  
             // Return any existing Action instance of this class  
             instance = (Action) actions.get(className);  
             if (instance != null) {  
                 if (log.isTraceEnabled()) {  
                     log.trace("  Returning existing Action instance");  
                 }  
                 return (instance);  
             }  
             // Create and return a new Action instance  
             if (log.isTraceEnabled()) {  
                 log.trace("  Creating new Action instance");  
             }  
             try {  
                 instance = (Action) RequestUtils.applicationInstance(className);  
                 // Maybe we should propagate this exception  
                 // instead of returning null.  
             } catch (Exception e) {  
                 log.error(getInternal().getMessage("actionCreate",  
                         mapping.getPath()), e);  
                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,  
                     getInternal().getMessage("actionCreate", mapping.getPath()));  
                 return (null);  
             }  
             actions.put(className, instance);  
             if (instance.getServlet() == null) {  
                 instance.setServlet(this.servlet);  
             }  
         }  
         return (instance);  
     }  

可以看到struts1的action是单例的。线程安全问题同servlet,如果action中使用不当的实例变量,就有可能存在线程安全问题。


下面是摘自http://struts.apache.org/1.x/userGuide/building_controller.html  4.4.1 Action Class Design Guidelines 部分

Write code for a multi-threaded environment - Our controller servlet createsonly one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets. Here are two general guidelines that will help you write scalable, thread-safe Action classes:
    // 这里说的很清楚,ActionServlet只创建一个Action实例,所有对这个Action的请求都是由这个实例来处理。与写Servlet类类似。

    * Only Use Local Variables - The most important principle that aids in thread-safe coding is touse only local variables, not instance variables , in your Action class. Local variables are created on a stack that is assigned (by your JVM) to each request thread, so there is no need to worry about sharing them. An Action can be factored into several local methods, so long as all variables needed are passed as method parameters. This assures thread safety, as the JVM handles such variables internally using the call stack which is associated with a single Thread.

      // 在Action类中只使用局部变量,不使用实例变量。因为局部变量是放在每个线程的独立栈中的,不是共享资源不存在线程安全问题;另外传入方法的参数变量也是不存在线程安全问题。
    * Conserve Resources - As a general rule, allocating scarce resources and keeping them across requests from the same user (in the user's session) can cause scalability problems. For example, if your application uses JDBC and you allocate a separate JDBC connection for every user, you are probably going to run in some scalability issues when your site suddenly shows up on Slashdot. You should strive to use pools and release resources (such as database connections) prior to forwarding control to the appropriate View component -- even if a bean method you have called throws an exception.
      // 这里说的是-优化资源,使用池技术。一时想明白这跟线程安全有什么关系。

http://hi.baidu.com/niujunkai/blog/item/021964adc130660a4a36d6ab.html

总结什么代码是线程安全的、是不需要同步的。如下:
1)常量始终是线程安全的,因为只存在读操作。
2)对构造器的访问(new 操作)是线程安全的,因为每次都新建一个实例,不会访问共享的资源。
3)最重要的是:局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量。

三、Struts2

Struts2为每个请求都实例化一个Action实例(Struts2+Spring框架中,spring的bean scope="singleton"以后另看),因此不存在多线程安全问题。

分析如下:

开始的FilterDispatcher和Dispatcher两个是单例,但在Dispatcher的serviceAction方法中创建的ActionProxy是局部变量,即只存在于各个线程的独立栈中,不是共享资源。

代码:

    Configuration config = configurationManager.getConfiguration();  
    // 这里一开始从Ioc容器Container中取到的Factory也是单例的  
    ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(  
            namespace, name, method, extraContext, true, false);  
    request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());  
    // if the ActionMapping says to go straight to a result, do it!  
    if (mapping.getResult() != null) {  
        Result result = mapping.getResult();  
        result.execute(proxy.getInvocation());  
    } else {  
        proxy.execute();  
    }  

同样在ActionProxyFactory创建Proxy过程中生成的ActionInvocation也是局部变量,只与本线程相关。

    ActionInvocation inv = new DefaultActionInvocation(extraContext, true);  
    container.inject(inv);  
    return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);  

创建实际Action的过程在ActionInvocation中:

action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);

// 里边最终就是一个clazz.newInstance();

在上面过程中值得关注的另一个与多线程有关的处理是ActionContext类

该类中用到了ThreadLocal类来维持只与本次请求相关的参数。(ThreadLocal见ThreadLocal类)

用ActionContext.getContext()即返回该ThreadLocal类,可以通过ctx.get(ActionContext.PARAMETERS),ctx.get(ActionContext.SESSION),ctx.get(ActionContext.APPLICATION)得到对应域的参数。

另外在看源码时发现DefaultActionInvocation的createResult()方法中用到了synchronized:

    public Result createResult() throws Exception {  
        if (explicitResult != null) {  
            Result ret = explicitResult;  
            explicitResult = null;  
            return ret;  
        }  
        ActionConfig config = proxy.getConfig();  
        Map<String, ResultConfig> results = config.getResults();  
        ResultConfig resultConfig = null;  
        // 这里为什么需要同步?  
        synchronized (config) {  
            try {  
                resultConfig = results.get(resultCode);  
            } catch (NullPointerException e) {  
                // swallow  
            }  
            if (resultConfig == null) {  
                // If no result is found for the given resultCode, try to get a wildcard '*' match.  
                resultConfig = results.get("*");  
            }  
        }  
        if (resultConfig != null) {  
            try {  
                return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());  
            } catch (Exception e) {  
                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);  
                throw new XWorkException(e, resultConfig);  
            }  
        } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {  
            return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);  
        }  
        return null;  
    }  


虽然这里results有可能与其他线程共享而HashMap又不是线程安全,但这里只有get操作,没见到有存操作,为什么还需要同步?


相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JSP中的pageEncoding和contentType的区别
  • 2016-wing的年度总结
  • java中split() replace() replaceAll()三个函数分析
  • SPOJ-COLONY - Linearian Colony!简单二分思想
  • msfconsole 控制台使用和操作
  • 数据库范式
  • sed awk grep三剑客常用
  • 数据库事务隔离级别
  • 全排序算法
  • 用Spring+Junit4.4进行测试(使用注解)
  • Java HashMap 分析四篇连载
  • Leetcode 144. Binary Tree Preorder Traversal
  • 单页web应用是什么?它又会给传统网站带来哪些好处?
  • 深入理解HTML协议
  • BootStrap学习笔记
  • JavaScript-如何实现克隆(clone)函数
  • 【node学习】协程
  • Android Volley源码解析
  • Angular数据绑定机制
  • java第三方包学习之lombok
  • js对象的深浅拷贝
  • node入门
  • PHP那些事儿
  • scrapy学习之路4(itemloder的使用)
  • spring + angular 实现导出excel
  • 对JS继承的一点思考
  • 力扣(LeetCode)21
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 悄悄地说一个bug
  • 如何在GitHub上创建个人博客
  • 使用API自动生成工具优化前端工作流
  • 算法-插入排序
  • 系统认识JavaScript正则表达式
  • ionic异常记录
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #mysql 8.0 踩坑日记
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (39)STM32——FLASH闪存
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (含笔试题)深度解析数据在内存中的存储
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (算法二)滑动窗口
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • ./configure,make,make install的作用
  • .NET 6 Mysql Canal (CDC 增量同步,捕获变更数据) 案例版
  • .NET Core 中插件式开发实现
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】