zoukankan      html  css  js  c++  java
  • Java中ThreadLocal的深入理解

    官方对ThreadLocal的描述:

      "该类提供了线程局部(thread-local)变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其get或set方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的private static字段,它们希望将状态与某一个线程(例如,用户ID或事物ID)相关联。"

    《Thinking in Java》中的描述:

      防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用变量的每个不同的线程都创建不同的存储。因此,如果你有5个线程都要使用变量x所表示的对象,那线程本地存储就会生成5个用于x的不同的存储块。主要是,它们使得你可以将状态与线程关联起来。

    三个要点:

      1.每个线程都有自己的局部变量

        每个线程都有一个独立于其它线程的上下文来保存这个变量,一个线程的本地变量对其它线程是不可见的。

      2.独立于变量的初始化副本

        ThreadLocal可以给一个初始值,而每个线程都会获得这个初始化值的一个副本,这样才能保证不同的线程都有一份拷贝。

      3.状态与某一个线程相关联

        ThreadLocal不是用于解决共享变量的问题的,不是为了协调线程同步而存在,而是为了方便每个线程处理自己的状态而引入的一个机制,就像《Thinking in Java》中描述的那样:”它们使得你可以将状态与线程关联起来。“理解这点对正确使用ThreadLocal至关重要。

    源码举例:

      从输出的结果可以看出,五个线程处理自己的本地变量值。

      而这个测试的结果,并没有相同。这是因为实例为对象,所以初始值为一个对象的引用,那么五个线程的副本就是这个对象的引用的副本,也就是说这些引用还是指向同一个对象,所以就出现了这种情况。

    ThreadLocal的接口方法:

    • void set(T value):设置当前线程的线程局部变量的值;
     1 public void set(T value) {
     2    Thread t = Thread.currentThread();
     3    ThreadLocalMap map = getMap(t);
     4 
     5    if (map != null)
     6      map.set(this, value);
     7   //说明线程第一次使用线程本地变量(注意这里的第一次含义)
     8    else
     9        createMap(t, value);
    10 }
    • public T get():该方法返回当前线程所对应的线程局部变量;
     1 public T get() {
     2   //获取当前执行线程
     3   Thread t = Thread.currentThread();
     4   //取得当前线程的ThreadLocalMap实例
     5   ThreadLocalMap map = getMap(t);
     6   //如果map不为空,说明该线程已经有了一个ThreadLocalMap实例
     7   if (map != null) {
     8     //map中保存线程的所有的线程本地变量,我们要去查找当前线程本地变量
     9     ThreadLocalMap.Entry e = map.getEntry(this);
    10     //如果当前线程本地变量存在这个map中,则返回其对应的值
    11     if (e != null)
    12          return (T)e.value;
    13   }
    14    //如果map不存在或者map中不存在当前线程本地变量,返回初始值
    15    return setInitialValue();
    16 }
    • public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。当线程结束后,对应该线程的局部变量将被自动垃圾回收,所以显示调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
    • protected T initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的,这个方法是一个延迟调用方法,在线程第1次调用get()或set(T)时才执行,并且仅执行1次。ThreadLocal中的默认实现直接返回一个null。

           这里注意,ThreadLocal中是有一个Map,但这个Map不是我们平时使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一个内部类,不对外使用的。当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值]这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量。下面提供一个简单的实现版本:

  • 相关阅读:
    [置顶] win7 && win8 下安装SqlServer 2008出现错误无法将对象实例化的问题
    [置顶] sizeof()和c++中变量们
    [置顶] sql2008 附加数据库 .mdf 出现错误 解决方案
    [置顶] 漂亮的 tab 样式
    [置顶] 认识指针和指针变量
    [置顶] SqlHelper类
    [置顶] 第一次使用事物 利用线性表
    [置顶] 设计模式之单例模式 (Design patterns of the The singleton pattern)c#
    [置顶] ListBox控件的数据绑定
    RedHat 6.0(64位)如何使用CentOS YUM源更新的方法
  • 原文地址:https://www.cnblogs.com/zhengbin/p/5674638.html
Copyright © 2011-2022 走看看