- 浏览: 142445 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (95)
- java (25)
- 数据库 (9)
- js (0)
- 框架 (10)
- 小问题 (8)
- 转载 (2)
- 配置开发环境 (6)
- 其它 (4)
- 功能记录 (2)
- 安全 (1)
- shell (3)
- spring ibaitis struts2 redis desc (1)
- spring security3.1 (1)
- 小问题 环境 (1)
- 小问题 环境 DelegatingFilterProxy cannot be cast to Filter (0)
- redis filter java (1)
- java redis (2)
- spring quartz 集群 批处理 (1)
- java mybatis (1)
- Junit soap (1)
- mock junit (0)
- mock junit spring (1)
- java jsoup (0)
- jsoup (1)
- python27 pycharm (1)
- maven pom (1)
- java log (1)
- mybatis 分页 (1)
- mysql (1)
- 业务 (1)
- java 测试 (1)
- java zookeeper 批处理 (1)
- hive hadoop (1)
- hive (1)
- hadoop (1)
- maven archetype (1)
- python rsa (1)
- python 页面分析 (1)
- python (0)
最新评论
-
wuyafeng123:
秒杀活动设计思路 -
indiajohns:
非常感谢,很快解决了我的问题
jmesa查询条件中文的编码转换问题 -
yujiaao:
太好了,感谢啊!这行在我这好象不行:URLDecoder.de ...
jmesa查询条件中文的编码转换问题 -
michael_wong:
呵呵,找到啦。在这里有全部源码:http://ishare. ...
Maven权威指南 的simple-parent 工程源码 -
michael_wong:
我也在找啊
Maven权威指南 的simple-parent 工程源码
http://www.javalobby.org/articles/thread-safe/index.jsp
利用servlet容器进行开发工作的开发人员应该知道servlet specifications 。
servlet容器为web.xml里面的对象产生一个单例servlet对象.请求有可能同时发生,这就意味着多个线程也许会同时执行一段代码。这就意味着web应用要特别重视安全问题。
回顾线程安全
首先让我们回顾一个线程安全的含义以及如何使用线程安全。各类文章基本上是这么说的:有段代码是线程安全的,它满足时reentrant 或者有通过互斥方式来保护它同步执行。
synchronized 是java线程安全的基础。但他容易带来阻塞。
阻塞对于web应用来说是一个大问题。阻塞并不是并发带来的唯一问题,还可能会有死锁发生。
除了synchronized 关键字,java还提供了java.util.Hashtable 和java.util.Vector.他们的方法是synchronized 的,所以在必要的时候才能用它们。否则就使用java.util.HashMap 和java.util.ArrayList,还有 java.util.Collections 也可以用。
Reentrance虽然容易管理,但也引入了一批问题。Reentrant 代码阻止数据在线程间共享。我们接下来看下面的代码,来理解线程安全
现在在执行这个方法时,每一个线程都有自己的独立的 栈区 。当线程进入到方法执行断的时候,一个方法变量在方法代码段中被创建,并保存在线程的 栈区 (静态方法也放在这里)。不同线程执行这段代码时,会有不同的a/b变量。所以这里是线程安全的,因为没有数据共享。
考虑一下下面的例子,多线程情况下只执行一次并可以重用结果:
这个地方虽然优化了,但可惜他不是线程安全的。两个线程并发执行的时候同时进入到pi==null这个位置,这样可能会new出一个脏的数据
Consider this example which uses ThreadLocal to make the method pi() again thread-safe while still offering performance gains:
ThreadLocal类封装了任何类型对象,并把它绑定到当前线程。线程执行pi()方法的时候,实例pi返回的是当前线程的对象。这样的调用是线程安全的。
之前人们对ThreadLocal有很多的怀疑,但是从上面这个例子被很好的保留。
Writing thread-safe code by reentrance requires you to be careful when using instance variables or static variables, especially when you are modifying objects that may be used by other threads. Synchronization may offer a outcome is some use cases. However, testing your application to identify performance bottlenecks that may be introduced by synchronization is only possible by using profiling tools and testing under load.
web应用中的 线程安全
下面我们考虑一下上面的这个事例如何用在线程安全的web应用中。我们新建一批可以有着良好定义的过程,通过这些过程,允许并发访问数据库。我们利用hibernate来做持久化对象模型。
我们现在只关注于数据库访问的线程安全问题,不关注web层页面访问的问题。类似于connections, result sets, statements or Hibernate sessions 的数据库对象都是有状态的对象。他们没有从设计角度实现线程安全。而且不能被多线程同时访问。我们要避免利用synchronization 方式来控制代码,也不用类似于vector和hashtable的同步类,这是为了避免出现死锁。相反,我们通过reentrance来实现线程安全。
Still, implementing thread-safe database access through reentrance remains a tedious task. Some clever people have come up with a solution by adding a filter in the servlet container configuration. Such a filter would amongst other things create a JDBC connection or Hibernate session at the start of the request before the web tier is invoked and bind it to the current thread by means of ThreadLocal for use in the business logic. While this approach would allow us to achieve thread-safe data access there still is the issue of managing transactions and database errors in addition to the required use a lot of boilerplate code in the business logic. Boilerplate code is particularly bad not only because we have to copy and paste it which makes our application hard to maintain but also because it's potentially buggy.
Spring comes to the rescue
Some of you may have heard of the data access abstraction offered by Spring, some may have used it. Spring's data access abstraction is known for declarative transaction demarcation and reuse of data access best practices through templates. One area that is less covered when reviewing Spring is the thread-safe way in which data access is achieved. This achievement is available in Spring for JDBC, Hibernate, JDO, iBatis and TopLink. Let's have look at how this works in our typical use case.
We start by defining a data source and session factory for Hibernate.
这是典型的方式使用hibernate配置数据源。我们定义数据源来连接数据库和本地会话实例(会话实例由hibernate会话工厂创建)。下一个目标就是添加一个通过hibernate访问数据库的业务对象,并添加一个事务管理器,这个事务管理器利用hibernate会话来管理本地事务。业务对象会暴露出一个在数据库中产生对象的方法,执行这个产生对象的操作的时机是在事务管理器在事务里面包装这个方法的时候。首选我们看一下这个事务对象和他的方法。
上面这个例子中,我们看到的HibernateTemplate 是spring提供的。这个template 类实现了下面的最佳实践和抽象的样例代码。... 类似的template类spring也为DBC, iBatis SqlMap, JDO and TopLink这些框架提供了。这些所有的template类和他们的实例变量都是通过reentrance 方式实现线程安全,而且能够安全的被多线程并发访问。这些模板的功能不仅仅是代码和最佳实践复用。更多的是满足我们线程安全的数据访问。下面让我们看看sping里面如何进行建立业务对象和进行事务管理。
从这里还看不出spring的事务管理。我们进一步来讨论这个配置文件。第一步,我们的业务对象调用HibernateCustomerDAO,此时的HibernateCustomerDAO 已经装配了hibernate会话工厂实例,注意点sping里面所有的bean都默认为单例的,所以可以看出,多线程执行createCustomer()方法是存在并发的。
第二步,我们注册一个同样组装了hibernate会话工厂的hibernate 事务管理器,这个事务管理器在每次调用的时候,都会做一系列的事情:1.先检查是否存在一个hibernate事务绑定到当前线程,如果有,就使用它,如果没有hibernate会话绑定到当前线程,事务管理器会请求hibernate session 工厂产生一个新的hibernate session,并把这个session 绑定到当前线程。2.依赖于我们自己定义的内容,如果当前没有事务,事务管理器针对hibernate session开始一个新的事务,若已经有事务了,加入到当前的事务中去。
这个动作能够以声明的方式,绑定到spring的TransactionProxyFactoryBean 。TransactionProxyFactoryBean 为通过事务管理器为管理事务的业务对象创建一个代理对象,每次createCustomer()方法都是通过proxy对象来调用的,事务的管理方式依赖于我们配置的事务属性。spring为hibernate、jdbc、djo等数据源提供一系列的事务管理实现
现在我们返回到我们的业务对象。下面部分依赖于spring的AOP。在我们调用createCustomer() 方法是,HibernateTemplate 会去找一个hibernate session绑定到当前线程上,因为我们把false传递给了HibernateTemplate 构造函数的第二个参数,如果没有hibernate session绑定在当前线程上,将会抛出一个unchecked exception,这样也是提供了另外一种针对没有注册transaction management而又想执行createCustomer()方法的安全控制网。我们是在讨论事务,如果transaction management上注册了hibernate session,transaction management应该绑定到了当前线程,而且事务已经处于开始状态。注意,HibernateTemplate 将不会检查一个事务当前是否保持活跃状态,或者这个事务标志为开始或者结束状态。同样要注意当前活动的事务在声明方法中如果抛出了异常,将会执行回滚。通过修改事务属性,我们可以改变事务动作行为,但这部分不属于本文讨论的内容。
结论
我们简单总结一下spring通过线程安全的数据访问功能。通过事务管理和ThreadLocal的强化,spring利用自己的data access templates和线程的ThreadLocal关联起数据库连接工具和当前线程,从这里我们可以看到,在同一个时间数据库连接并不是在多线程之间共享的。spring所做的,只是提供 1.声明式事务管理;2抽象的容器平台级的代码;3.最佳实践,4.线程安全。在通过使用spring来建立数据库连接的时候,我们的应用利用reentrance 来实现线程安全的功能也是顺理成章的。
利用servlet容器进行开发工作的开发人员应该知道servlet specifications 。
servlet容器为web.xml里面的对象产生一个单例servlet对象.请求有可能同时发生,这就意味着多个线程也许会同时执行一段代码。这就意味着web应用要特别重视安全问题。
回顾线程安全
首先让我们回顾一个线程安全的含义以及如何使用线程安全。各类文章基本上是这么说的:有段代码是线程安全的,它满足时reentrant 或者有通过互斥方式来保护它同步执行。
synchronized 是java线程安全的基础。但他容易带来阻塞。
阻塞对于web应用来说是一个大问题。阻塞并不是并发带来的唯一问题,还可能会有死锁发生。
除了synchronized 关键字,java还提供了java.util.Hashtable 和java.util.Vector.他们的方法是synchronized 的,所以在必要的时候才能用它们。否则就使用java.util.HashMap 和java.util.ArrayList,还有 java.util.Collections 也可以用。
Reentrance虽然容易管理,但也引入了一批问题。Reentrant 代码阻止数据在线程间共享。我们接下来看下面的代码,来理解线程安全
public Double pi() { int a = 22; int b = 7; return new Double(a / b); }
现在在执行这个方法时,每一个线程都有自己的独立的 栈区 。当线程进入到方法执行断的时候,一个方法变量在方法代码段中被创建,并保存在线程的 栈区 (静态方法也放在这里)。不同线程执行这段代码时,会有不同的a/b变量。所以这里是线程安全的,因为没有数据共享。
考虑一下下面的例子,多线程情况下只执行一次并可以重用结果:
private Double pi = null; public Double pi() { if (pi == null) { pi = new Double(22 / 7); } return pi; }
这个地方虽然优化了,但可惜他不是线程安全的。两个线程并发执行的时候同时进入到pi==null这个位置,这样可能会new出一个脏的数据
Consider this example which uses ThreadLocal to make the method pi() again thread-safe while still offering performance gains:
private static ThreadLocal pi = new ThreadLocal(); public Double pi() { if (pi.get() == null) { pi.set(new Double(22 / 7)); } return (Double)pi.get(); }
ThreadLocal类封装了任何类型对象,并把它绑定到当前线程。线程执行pi()方法的时候,实例pi返回的是当前线程的对象。这样的调用是线程安全的。
之前人们对ThreadLocal有很多的怀疑,但是从上面这个例子被很好的保留。
Writing thread-safe code by reentrance requires you to be careful when using instance variables or static variables, especially when you are modifying objects that may be used by other threads. Synchronization may offer a outcome is some use cases. However, testing your application to identify performance bottlenecks that may be introduced by synchronization is only possible by using profiling tools and testing under load.
web应用中的 线程安全
下面我们考虑一下上面的这个事例如何用在线程安全的web应用中。我们新建一批可以有着良好定义的过程,通过这些过程,允许并发访问数据库。我们利用hibernate来做持久化对象模型。
我们现在只关注于数据库访问的线程安全问题,不关注web层页面访问的问题。类似于connections, result sets, statements or Hibernate sessions 的数据库对象都是有状态的对象。他们没有从设计角度实现线程安全。而且不能被多线程同时访问。我们要避免利用synchronization 方式来控制代码,也不用类似于vector和hashtable的同步类,这是为了避免出现死锁。相反,我们通过reentrance来实现线程安全。
Still, implementing thread-safe database access through reentrance remains a tedious task. Some clever people have come up with a solution by adding a filter in the servlet container configuration. Such a filter would amongst other things create a JDBC connection or Hibernate session at the start of the request before the web tier is invoked and bind it to the current thread by means of ThreadLocal for use in the business logic. While this approach would allow us to achieve thread-safe data access there still is the issue of managing transactions and database errors in addition to the required use a lot of boilerplate code in the business logic. Boilerplate code is particularly bad not only because we have to copy and paste it which makes our application hard to maintain but also because it's potentially buggy.
Spring comes to the rescue
Some of you may have heard of the data access abstraction offered by Spring, some may have used it. Spring's data access abstraction is known for declarative transaction demarcation and reuse of data access best practices through templates. One area that is less covered when reviewing Spring is the thread-safe way in which data access is achieved. This achievement is available in Spring for JDBC, Hibernate, JDO, iBatis and TopLink. Let's have look at how this works in our typical use case.
We start by defining a data source and session factory for Hibernate.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>WEB-INF/jdbc.properties</value> </list> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"><value>${jdbc.driverClassName}</value></property> <property name="url"><value>${jdbc.url}</value></property> <property name="username"><value>${jdbc.username}</value></property> <property name="password"><value>${jdbc.password}</value></property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource"/> </property> <property name="mappingDirectoryLocations"> <list> <value>classpath:</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean>
这是典型的方式使用hibernate配置数据源。我们定义数据源来连接数据库和本地会话实例(会话实例由hibernate会话工厂创建)。下一个目标就是添加一个通过hibernate访问数据库的业务对象,并添加一个事务管理器,这个事务管理器利用hibernate会话来管理本地事务。业务对象会暴露出一个在数据库中产生对象的方法,执行这个产生对象的操作的时机是在事务管理器在事务里面包装这个方法的时候。首选我们看一下这个事务对象和他的方法。
public interface CustomerDAO { public void createCustomer(Customer customer); } public class HibernateCustomerDAO implements CustomerDAO { private HibernateTemplate hibernateTemplate = null; public void setSessionFactory(SessionFactory sessionFactory) { this.hibernateTemplate = new HibernateTemplate(sessionFactory, false); } public void createCustomer(Customer customer) { this.hibernateTemplate.save(customer); } }
上面这个例子中,我们看到的HibernateTemplate 是spring提供的。这个template 类实现了下面的最佳实践和抽象的样例代码。... 类似的template类spring也为DBC, iBatis SqlMap, JDO and TopLink这些框架提供了。这些所有的template类和他们的实例变量都是通过reentrance 方式实现线程安全,而且能够安全的被多线程并发访问。这些模板的功能不仅仅是代码和最佳实践复用。更多的是满足我们线程安全的数据访问。下面让我们看看sping里面如何进行建立业务对象和进行事务管理。
<bean id="customerDAOTarget" class="test.usecase.HibernateCustomerDAO"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager"> <property name="sessionFactory"><ref bean="sessionFactory"/></property> </bean> <bean id="customerDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"><ref bean="transactionManager"/></property> <property name="target"><ref bean="customerDAOTarget"/></property> <property name="transactionAttributes"> <props> <prop key="create*">PROPAGATION_REQUIRED</prop> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
从这里还看不出spring的事务管理。我们进一步来讨论这个配置文件。第一步,我们的业务对象调用HibernateCustomerDAO,此时的HibernateCustomerDAO 已经装配了hibernate会话工厂实例,注意点sping里面所有的bean都默认为单例的,所以可以看出,多线程执行createCustomer()方法是存在并发的。
第二步,我们注册一个同样组装了hibernate会话工厂的hibernate 事务管理器,这个事务管理器在每次调用的时候,都会做一系列的事情:1.先检查是否存在一个hibernate事务绑定到当前线程,如果有,就使用它,如果没有hibernate会话绑定到当前线程,事务管理器会请求hibernate session 工厂产生一个新的hibernate session,并把这个session 绑定到当前线程。2.依赖于我们自己定义的内容,如果当前没有事务,事务管理器针对hibernate session开始一个新的事务,若已经有事务了,加入到当前的事务中去。
这个动作能够以声明的方式,绑定到spring的TransactionProxyFactoryBean 。TransactionProxyFactoryBean 为通过事务管理器为管理事务的业务对象创建一个代理对象,每次createCustomer()方法都是通过proxy对象来调用的,事务的管理方式依赖于我们配置的事务属性。spring为hibernate、jdbc、djo等数据源提供一系列的事务管理实现
现在我们返回到我们的业务对象。下面部分依赖于spring的AOP。在我们调用createCustomer() 方法是,HibernateTemplate 会去找一个hibernate session绑定到当前线程上,因为我们把false传递给了HibernateTemplate 构造函数的第二个参数,如果没有hibernate session绑定在当前线程上,将会抛出一个unchecked exception,这样也是提供了另外一种针对没有注册transaction management而又想执行createCustomer()方法的安全控制网。我们是在讨论事务,如果transaction management上注册了hibernate session,transaction management应该绑定到了当前线程,而且事务已经处于开始状态。注意,HibernateTemplate 将不会检查一个事务当前是否保持活跃状态,或者这个事务标志为开始或者结束状态。同样要注意当前活动的事务在声明方法中如果抛出了异常,将会执行回滚。通过修改事务属性,我们可以改变事务动作行为,但这部分不属于本文讨论的内容。
结论
我们简单总结一下spring通过线程安全的数据访问功能。通过事务管理和ThreadLocal的强化,spring利用自己的data access templates和线程的ThreadLocal关联起数据库连接工具和当前线程,从这里我们可以看到,在同一个时间数据库连接并不是在多线程之间共享的。spring所做的,只是提供 1.声明式事务管理;2抽象的容器平台级的代码;3.最佳实践,4.线程安全。在通过使用spring来建立数据库连接的时候,我们的应用利用reentrance 来实现线程安全的功能也是顺理成章的。
发表评论
-
详解ReentrantReadWriteLock
2016-04-21 14:34 0package java.util.concurrent. ... -
java内部类使用场景体会
2016-04-21 11:56 912内部类的作用: 1.完善多重继承。 疑问在于:多重继承可以用 ... -
logBack用法,记录
2015-11-24 15:37 9921. Logback使用手册 系统中的日志采用 slf4j ... -
表产生sequence功能,测试并发
2015-11-18 16:21 615@Test public void testSerial ... -
Demo
2014-11-03 21:20 0package com.xly.jsoup.nbjy; /* ... -
反射小计
2014-10-07 11:32 488Test.java方法dd重载 class Test{ ... -
Btrace测试
2014-07-17 17:56 599偶尔看到BTrace,必须记录一下 利用hotSpot虚拟机 ... -
java对象占用heap的大小
2013-07-31 10:09 906一个对象占用的堆空间首先是和它有多少个成员变量直接相关 pub ... -
BeanCopier和BeanUtils实现对象复制,处理date类型
2013-03-29 12:52 4376没有废话 先是BeanUtils的实现类型: impo ... -
liferay 为JournalArticle添加分类
2013-03-21 15:45 1056/** * 为网页内容添加分类 ... -
liferay直接跳转到网页内容编辑页面修改的friendlyUrl效果
2013-03-21 14:11 1209找到portlat-impl.jar包,在目录 port ... -
JMS_DEMO
2013-03-14 20:25 14671.安装activeMq 修改service.pr ... -
Xmemcached/jedis springdemo
2013-02-20 13:13 893Xmemcached: HelloWordTest.ja ... -
规则检查功能的demo
2013-01-31 12:55 0规则检查Demo,包括mina,线程池功能 -
JAVA虚拟机锁机制的升级流程
2013-01-08 14:52 2833http://ifeve.com/java-synchroni ... -
NIO学习
2012-11-27 11:49 1460最需要关注的是,应用进程、内核、IO三个概念。 所谓java的 ... -
Tomcat的HttpConnector/HttpProcesser(tomcat源码学习)
2012-11-21 10:47 1222先启动进程HttpProcesser,等请求到达后加入sock ... -
web.xml加载
2011-02-18 17:24 881转: web.xml文件内容加载顺序为:context- pa ... -
Maven权威指南 的simple-parent 工程源码
2010-11-30 10:12 1536未能找到全部源码,知道的朋友请分享。 -
webLogic中 Cannot parse POST parameters of request: 的问题小记
2010-11-25 13:25 7760报错内容: weblogic.utils.NestedRunt ...
相关推荐
r下载后,解压,把解压后的文件直接放入到red5webaaps下
ImageMagick on App Service (Windows)
#Spring Sample Apps 基于 JAVA 和 spring-mvc 和 ioc 的 Web 示例应用程序,以 CouchDb 和 ektorp 作为 orm。
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
typesafeconfig-extensions.zip,Web应用程序的合理默认配置工厂。帮助利用类型安全配置的库
Eclipse中将web项目自动发布到Tomcat_webapps下
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
完美支持was8.5中间件,包内有使用说明书,这个是修改版,改数据源就可以使用
oauth2的示例工程源代码,含build好的war包 来源于github,但build会很耗时间 直接取出2个target目录下的war文件 ...置于webapps下 启动tomcat后,访问http://localhost:8080/tonr2 即可体验演示工程
tomcat-webapps-7.0.76-7.el7_5.noarch.rpm
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot...
防止在删除Tomcat下工程时误删 webapps下面自带的API、Index、Manager导致localhost:8080本地页面无法打开,上传了一下,免得再去重新安装
一个简单的 Spring Boot web 应用程序,它使用作为模板语言。 用法 在运行此 webapp 之前,您必须确保其中一个scratch-*-rest webapps 正在localhost上运行。 一个很好的例子是 。 然后,您可以使用以下命令启动 ...
它允许你用script脚本的方式提供web服务,同时在脚本里能够调用spring. 把文件下载下来,放到tomcat的\tomcat6.0\webapps目录,直接启动就可以访问,例子了:...
Yii2 Web应用程序 Yii2 Web应用程序是一个专用于yii2开发人员社区的入门项目,它具有满足快速创建应用程序需求和额外安全性的功能。该项目的建造: AdminLTE 3 Yii2最新版本 Bootstrap 4 成为开源集体贡献者:安装$ ...
Struts2.0_Hibernate3_spring2.0 部署Web应用请按如下步骤进行: 1. 进入reg_login路径下,将mysql.sql脚本中的语句导入MySQL数据库。 2. 修改reg_login\WEB-INF路径下的applicationContext.xml文件,将其中的...
官方最新版的resin服务器,可与Apache,IIS服务器集成。最新版核心配置文件已经改为xml
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
信息办公玉玺学生信息管理系统-webapps.zip
WebApps Sandboxed browser Android app This Android app is a fork of the GoogleApps Sandboxed browser. The idea behind it is to provide a secure way to browse popular webapps by eliminating referrers...