1、首先,搭建好Spring项目。然后创建对应的Controller、UserService和UserDao类。这里,Controller、UserService和UserDao都定义了自己的变量,分别为a、us和ud。这里,Controller、UserService和UserDao都采用了单例模式。
2、我们分别用Chrome和Firefox访问test1.jspx,会得到如下图的结果:
3、这里,我们是先在Firefox中访问test1.jspx,等待2秒钟后,在从Chrome访问。可以看到:(1) 两个浏览器各自对应着自己的线程;(2) 从Controller -> Service -> Dao整个过程,都是一个线程!(3) 由于Controller、Service和Dao都配置为单例模式,而从结果来看,a、us、ud出现了线程安全的问题。可以证明,单例模式下,Controller、UserService和UserDao都不是线程安全的。这里,a值相等是因为,Firefox对a加1之后,等待了5秒钟让Chrome进入,Chrome进入修改的速度是很快的,Chrome也对a加1,此时,a变为2。然后,Firefox和Chrome分别返回,所以a值都为2。
4、要解决三者的线程安全问题,可以考虑采用prototype模式,或者使用ThreadLocal。
5、我们对Controller进行ThreadLocal改进(如上图所示),改进后,依然按照先Firefox后Chrome的顺序进行访问:可以看出,a值现在已经是独立的了,Firefox和Chrome各自拥有自己的a值。而没有使用ThreadLocal改造的UserDao和UserService依然存在线程安全问题。
6、ThreadLocal用于改造Session的一个经典例子:private static final ThreadLocal threadSession = new ThreadLocal();public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s;}