zoukankan      html  css  js  c++  java
  • ThreadLocal详解

     

      ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。

      这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。

      我们从源码的角度来分析这个问题。

      首先定义一个ThreadLocal:

     1 public class ConnectionUtil {
     2     private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
     3     private static Connection initConn = null;
     4     static {
     5         try {
     6             initConn = DriverManager.getConnection("url, name and password");
     7         } catch (SQLException e) {
     8             e.printStackTrace();
     9         }
    10     }
    11     
    12     public Connection getConn() {
    13         Connection c = tl.get();
    14         if(null == c) tl.set(initConn);
    15         return tl.get();
    16     }
    17     
    18 }

    这样子,都是用同一个连接,但是每个连接都是新的,是同一个连接的副本。

      那么实现机制是如何的呢?

      1、每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal。

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    2、当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。

     1 public T get() {
     2     Thread t = Thread.currentThread();
     3     ThreadLocalMap map = getMap(t);
     4     if (map != null) {
     5         ThreadLocalMap.Entry e = map.getEntry(this);
     6         if (e != null)
     7             return (T)e.value;
     8     }
     9     return setInitialValue();
    10 }

    3、当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。

      4、总结:当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。

      5、应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。

      6、其实说再多也不如看一下源码来得清晰。如果要看源码,其中涉及到一个WeakReference和一个Map,这两个地方需要了解下,这两个东西分别是a.Java的弱引用,也就是GC的时候会销毁该引用所包裹(引用)的对象,这个threadLocal作为key可能被销毁,但是只要我们定义成他的类不卸载,tl这个强引用就始终引用着这个ThreadLocal的,永远不会被gc掉。b.和HashMap差不多。

      事实上,从本质来讲,就是每个线程都维护了一个map,而这个map的key就是threadLocal,而值就是我们set的那个值,每次线程在get的时候,都从自己的变量中取值,既然从自己的变量中取值,那肯定就不存在线程安全问题,总体来讲,ThreadLocal这个变量的状态根本没有发生变化,他仅仅是充当一个key的角色,另外提供给每一个线程一个初始值。如果允许的话,我们自己就能实现一个这样的功能,只不过恰好JDK就已经帮我们做了这个事情。

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/UniqueColor/p/7411271.html
Copyright © 2011-2022 走看看