深入研究java.lang.ThreadLocal类

来源:互联网 发布:编程 儿童 编辑:程序博客网 时间:2024/06/10 06:36
一、概述
      ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
      从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
      通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
     ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
     概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
  
二、典型实例
1、Hiberante的Session 工具类HibernateUtil
     这个类是Hibernate官方文档中HibernateUtil类,用于session管理。
 Java代码  收藏代码
  1. public class HibernateUtil {  
  2.     private static Log log = LogFactory.getLog(HibernateUtil.class);  
  3.     private static final SessionFactory sessionFactory;     //定义SessionFactory  
  4.    
  5.     static {  
  6.         try {  
  7.             // 通过默认配置文件hibernate.cfg.xml创建SessionFactory  
  8.             sessionFactory = new Configuration().configure().buildSessionFactory();  
  9.         } catch (Throwable ex) {  
  10.             log.error("初始化SessionFactory失败!", ex);  
  11.             throw new ExceptionInInitializerError(ex);  
  12.         }  
  13.     }  
  14.   
  15.     //创建线程局部变量session,用来保存Hibernate的Session  
  16.     public static final ThreadLocal session = new ThreadLocal();  
  17.    
  18.     /** 
  19.      * 获取当前线程中的Session 
  20.      * @return Session 
  21.      * @throws HibernateException 
  22.      */  
  23.     public static Session currentSession() throws HibernateException {  
  24.         Session s = (Session) session.get();  
  25.         // 如果Session还没有打开,则新开一个Session  
  26.         if (s == null) {  
  27.             s = sessionFactory.openSession();  
  28.             session.set(s);         //将新开的Session保存到线程局部变量中  
  29.         }  
  30.         return s;  
  31.     }  
  32.    
  33.     public static void closeSession() throws HibernateException {  
  34.         //获取线程局部变量,并强制转换为Session类型  
  35.         Session s = (Session) session.get();  
  36.         session.set(null);  
  37.         if (s != null)  
  38.             s.close();  
  39.     }  
  40. }  
            在这个类中,由于没有重写ThreadLocal的initialValue()方法,则首次创建线程局部变量session其初始值为null,第一次调用currentSession()的   时候,线程局部变量的get()方法也为null。因此,对session做了判断,如果       为null,则新开一个Session,并保存到线程局部变量session中,这一步  非常的关键,这也是“public static final ThreadLocal session = new ThreadLocal()”所创建对象session能强制转换为Hibernate Session对象的原因。
 
2、另外一个实例
    创建一个Bean,通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。
Java代码  收藏代码
  1. /** 
  2.  * Created by IntelliJ IDEA. 
  3.  * User: leizhimin 
  4.  * Date: 2007-11-23 
  5.  * Time: 10:45:02 
  6.  * 学生 
  7.  */  
  8. public class Student {  
  9.     private int age = 0;   //年龄  
  10.    
  11.     public int getAge() {  
  12.         return this.age;  
  13.     }  
  14.    
  15.     public void setAge(int age) {  
  16.         this.age = age;  
  17.     }  
  18. }  
 
Java代码  收藏代码
  1. /** 
  2.  * Created by IntelliJ IDEA. 
  3.  * User: leizhimin 
  4.  * Date: 2007-11-23 
  5.  * Time: 10:53:33 
  6.  * 多线程下测试程序 
  7.  */  
  8. public class ThreadLocalDemo implements Runnable {  
  9.     //创建线程局部变量studentLocal,在后面你会发现用来保存Student对象  
  10.     private final static ThreadLocal studentLocal = new ThreadLocal();  
  11.    
  12.     public static void main(String[] agrs) {  
  13.         ThreadLocalDemo td = new ThreadLocalDemo();  
  14.         Thread t1 = new Thread(td, "a");  
  15.         Thread t2 = new Thread(td, "b");  
  16.         t1.start();  
  17.         t2.start();  
  18.     }  
  19.    
  20.     public void run() {  
  21.         accessStudent();  
  22.     }  
  23.    
  24.     /** 
  25.      * 示例业务方法,用来测试 
  26.      */  
  27.     public void accessStudent() {  
  28.         //获取当前线程的名字  
  29.         String currentThreadName = Thread.currentThread().getName();  
  30.         System.out.println(currentThreadName + " is running!");  
  31.         //产生一个随机数并打印  
  32.         Random random = new Random();  
  33.         int age = random.nextInt(100);  
  34.         System.out.println("thread " + currentThreadName + " set age to:" + age);  
  35.         //获取一个Student对象,并将随机数年龄插入到对象属性中  
  36.         Student student = getStudent();  
  37.         student.setAge(age);  
  38.         System.out.println("thread " + currentThreadName + " first read age is:" + student.getAge());  
  39.         try {  
  40.             Thread.sleep(500);  
  41.         }  
  42.         catch (InterruptedException ex) {  
  43.             ex.printStackTrace();  
  44.         }  
  45.         System.out.println("thread " + currentThreadName + " second read age is:" + student.getAge());  
  46.     }  
  47.    
  48.     protected Student getStudent() {  
  49.         //获取本地线程变量并强制转换为Student类型  
  50.         Student student = (Student) studentLocal.get();  
  51.         //线程首次执行此方法的时候,studentLocal.get()肯定为null  
  52.         if (student == null) {  
  53.             //创建一个Student对象,并保存到本地线程变量studentLocal中  
  54.             student = new Student();  
  55.             studentLocal.set(student);  
  56.         }  
  57.         return student;  
  58.     }  
  59. }  
 
运行结果:
a is running! 
thread a set age to:76 
b is running! 
thread b set age to:27 
thread a first read age is:76 
thread b first read age is:27 
thread a second read age is:76 
thread b second read age is:27 
 
可以看到a、b两个线程age在不同时刻打印的值是完全相同的。这个程序通过妙用ThreadLocal,既实现多线程并发,游兼顾数据的安全性。
 
三、总结
      ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
      ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。
      ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
      Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。
      当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
 
四、ThreadLocal使用的一般步骤
     1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
     2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
     3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

  
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 无线路由我用手机上网网速慢怎么办 下载的软件安装包以丢失怎么办 战舰世界航母的飞机恐惧状态怎么办 cad打开图纸不显示轴号怎么办 若背包忘在服务区没拿怎么办 使劲擤鼻涕耳朵耳朵疼了怎么办 用力擤鼻涕一侧的脸肿了怎么办 擦鼻涕太用力耳朵塞住了怎么办 宝宝鼻腔里有鼻涕呼呼响怎么办 黏痰在嗓子眼很干出不来怎么办 宝宝生病好了不久突然又咳嗽怎么办 7个月的宝宝经常生病怎么办 擤鼻涕鼻子周围红肿爆皮怎么办 洗衣机有鼻涕虫洗过的衣服怎么办 手机丢了里边有穿内衣照片怎么办 脸上不知是过敏还是湿疹流水怎么办 一个月的宝宝鼻子不通气怎么办 六个月的宝宝有清水鼻涕怎么办 一岁三个月宝宝流清鼻涕怎么办 宝宝流清水鼻涕怎么办最简单方法 8个月宝宝流清鼻涕怎么办 7个月宝宝流清鼻涕怎么办 9个月宝宝流清鼻涕怎么办 十一个月宝宝流清鼻涕怎么办 18个月宝宝咳嗽有痰怎么办 2个月宝宝鼻子不通气怎么办 两个月的宝宝鼻塞不通气怎么办 两个月大的宝宝鼻子不通气怎么办 宝宝6个月咳嗽有痰怎么办 6个月的宝宝有痰怎么办 小孩流有点咳嗽和脓鼻涕怎么办 6个月的宝宝流鼻涕该怎么办? 小孩鼻塞不流鼻涕总吸鼻子怎么办 四个多月宝宝感冒鼻塞严重怎么办 怀孕6个月严重感冒鼻塞怎么办 16个月的宝宝上火流鼻血怎么办 小狗咳嗽打了针还不好怎么办 孕妇感冒后鼻涕带血口腔发炎怎么办 宝宝出生17天睡眠不安稳怎么办 月子里宝宝睡觉老是睡不安稳怎么办 孕妇晚期咳嗽鼻涕黄咽喉痛怎么办