对web项目写法的一点点领悟
我们访问一个web应用系统的流程一般是这样的:
1.用户输入相应的url地址,web容器首先接受到了用户的请求。然后根据加载的web.xml中映射的地址把请求的参数封装到相应的对象中,然后以方法参数的方式传递给应用程序。
2.应用程序接受到了参数进行一系列的逻辑处理然后再返回给用户。
这个过程基本上所有的java开发人员都很熟悉。我以前对于网上说的一些结论不是很好的能理解。结论如下:
1.Servlet是线程不安全的
2.Servlet是单例的
以前没有仔细去思考这些,只是把结论记住了。今天来好好分析和梳理下为什么是这样的。
什么是线程安全的,网上答案很多。其中有一个比较重要:局部变量。局部变量是各个线程私有的。也就是通常所说的线程安全的。为什么呢?因为局部变量是在方法中定义的变量。局部变量保存在栈区中。
根据上面的jvm内存结构说明可以看出,栈区是每个线程私有的。所以局部变量才能保证是线程安全的。
而方法的参数同样是每个线程私有的。
理解了上面的原理,再来想想我们平时的web项目。在使用了mvc框架的时候,一般习惯的写法都是:
controller层把service层的变量定义成成员变量、service层把dao层的变量定义成成员变量。然后使用spring的注入方式去实例化成员变量。而且需要注意的是:serlvet并不是每次都创建一个新的对象的,在web容器中使用了单例模式。spring实例化类的时候如果没有特殊配置,默认也是单例模式的。
这样问题就出现了:成员变量不是线程安全的,但为什么我们在编写web项目的时候都这样写呢?单例本身有可能会造成线程不安全为什么web容器和spring都会使用呢?
我的理解是:单例虽然可能线程不安全,但是性能要高。因为只创建了一个对象。然后再仔细想想我们的使用过程,spring注入bean之后我们一直在使用很少去修改。再想想什么情况下会产生并发问题:当多个线程对同一资源进行变更的时候会出现问题。如果多个线程对同一个资源只是读取是不会有任何问题的。所以我们这样的编写习惯是不会产生并发问题的。
当然如果自己去定义成员变量,要对成员变量去变更操作的话。还是需要考虑并发问题的。