Spring使用ThreadLocal解决线程安全问题

http://www.iteye.com/topic/1123824



我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全的“状态性对象”采用ThreadLocal进行封装,让它们也成为线程安全的“状态性对象”,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。 

一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程。

这样用户就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有对象所访问的同一ThreadLocal变量都是当前线程所绑定的。 
下面的实例能够体现Spring对有状态Bean的改造思路: 

代码清单9-5  TopicDao:非线程安全 

Java代码   收藏代码
  1. public class TopicDao {  
  2.    //①一个非线程安全的变量  
  3.    private Connection conn;   
  4.    public void addTopic(){  
  5.         //②引用非线程安全变量  
  6.        Statement stat = conn.createStatement();  
  7.        …  
  8.    }  
  9. }  


由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:


代码清单9-6  TopicDao:线程安全 

Java代码   收藏代码
  1. import java.sql.Connection;  
  2. import java.sql.Statement;  
  3. public class TopicDao {  
  4.   
  5.   //①使用ThreadLocal保存Connection变量  
  6. private static ThreadLocal connThreadLocal = new ThreadLocal(); 
  7.  
  8. public static Connection getConnection(){             
  9.         //②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,  
  10.         //并将其保存到线程本地变量中。  
  11.       if (connThreadLocal.get() == null) {  
  12.             Connection conn = ConnectionManager.getConnection();  
  13.             connThreadLocal.set(conn);  
  14.               return conn;  
  15.         }else{  
  16.               //③直接返回线程本地变量  
  17.             return connThreadLocal.get();  
  18.         }  
  19.  
  20.  public void addTopic() {  
  21.         //④从ThreadLocal中获取线程对应的  
  22.          Statement stat = getConnection().createStatement();  
  23.     }  
  24. }  


不同的线程在使用TopicDao时,先判断connThreadLocal.get()是否为null,如果为null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其他线程的Connection。因此,这个TopicDao就可以做到singleton共享了。 

当然,这个例子本身很粗糙,将Connection的ThreadLocal直接放在Dao只能做到本Dao的多个方法共享Connection时不发生线程安全问题,但无法和其他Dao共用同一个Connection,要做到同一事务多Dao共享同一个Connection,必须在一个共同的外部类使用ThreadLocal保存Connection。但这个实例基本上说明了Spring对有状态类线程安全化的解决思路。在本章后面的内容中,我们将详细说明Spring如何通过ThreadLocal解决事务管理的问题。 

这些文章摘自于我的《Spring 3.x企业应用开发实战》,我将通过连载的方式,陆续在此发出。欢迎大家讨论。 


转载于:https://www.cnblogs.com/leeeee/p/7276222.html

你可能感兴趣的:(Spring使用ThreadLocal解决线程安全问题)