温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

ThreadLocal怎么在Java项目中使用

发布时间:2021-03-25 16:57:20 来源:亿速云 阅读:252 作者:Leah 栏目:编程语言

ThreadLocal怎么在Java项目中使用?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

ThreadLocal是在多线程环境下经常使用的一个类。

ThreadLocal适用的场景是,多个线程都需要使用一个变量,但这个变量的值不需要在各个线程间共享,各个线程都只使用自己的这个变量的值。这样的场景下,可以使用ThreadLocal。此外,我们使用ThreadLocal还能解决一个参数过多的问题。例如一个线程内的某个方法f1有10个参数,而f1调用f2时,f2又有10个参数,这么多的参数传递十分繁琐。那么,我们可以使用ThreadLocal来减少参数的传递,用ThreadLocal定义全局变量,各个线程需要参数时,去全局变量去取就可以了。

接下来我们看一下ThreadLocal的源码。首先是类的介绍。如下图。这个类提供了线程本地变量。这些变量使每个线程都有自己的一份拷贝。ThreadLocal期望能够管理一个线程的状态,例如用户id或事务id。例如下面的例子产生线程本地的唯一id。线程的id是第一次调用时进行复制,并且在后面的调用中保持不变。

This class provides thread-local variables. 
These variables differ from their normal counterparts in that each thread that accesses
 one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that
 wish to associate state with a thread (e.g., a user ID or Transaction ID).
For example, the class below generates unique identifiers local to each thread.
 A thread's id is assigned the first time it invokes ThreadId.get() and 
remains unchanged on subsequent calls.
  import java.util.concurrent.atomic.AtomicInteger;
  public class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);
    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
      new ThreadLocal<Integer>() {
        @Override protected Integer initialValue() {
          return nextId.getAndIncrement();
      }
    };
    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
      return threadId.get();
    }
  }
Each thread holds an implicit reference to its copy of a thread-local 
variable as long as the thread is alive and the ThreadLocal instance is 
accessible; after a thread goes away, all of its copies of thread-local
 instances are subject to garbage collection (unless other references to 
these copies exist).

下面看一下set方法。

set方法的作用是,把线程本地变量的当前线程的拷贝设置为指定的值。大部分子类无需重写该方法。首先获取当前线程,然后获取当前线程的ThreadLocalMap。如果ThreadLocalMap不为null,则设置当前线程的值为指定的值,否则调用createMap方法。

获取线程的ThreadLocalMap对象,是直接返回的线程的threadLocals,类型为ThreadLocalMap。也就是说,每个线程都有一个ThreadLocalMap对象,用于保存该线程关联的所有的ThreadLocal类型的变量。ThreadLocalMap的key是ThreadLocal,value是该ThreadLocal对应的值。具体什么意思呢?在程序中,我们可以定义不止一个ThreadLocal对象,一般会有多个,比如定义3个ThreadLocal<String>,再定义2个ThreadLocal<Integer>,而每个线程可能都需要访问全部这些ThreadLocal的变量,那么,我们用什么数据结构来实现呢?当然,最好的方式就是像源码中的这样,每个线程有一个ThreadLocalMap,key为ThreadLocal变量名,而value为该线程在该ThreadLocal变量的值。这个设计实在是太巧妙了。

写到这里,自己回想起之前换工作面试时,面试官问自己关于ThreadLocal的实现原理。那个时候,为了准备面试,自己只在网上看了一些面试题,并没有真正掌握,在回答这个问题时,我有印象,自己回答的是用一个map,线程的id值作为key,变量值作为value,诶,露馅了啊。

  /**
   * Sets the current thread's copy of this thread-local variable
   * to the specified value. Most subclasses will have no need to
   * override this method, relying solely on the {@link #initialValue}
   * method to set the values of thread-locals.
   * @param value the value to be stored in the current thread's copy of
   *    this thread-local.
   **/
  public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
      map.set(this, value);
    else
      createMap(t, value);
  }
  /**
   * Get the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   * @param t the current thread
   * @return the map
   **/
  ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
  }
  /**
   * Create the map associated with a ThreadLocal. Overridden in
   * InheritableThreadLocal.
   * @param t the current thread
   * @param firstValue value for the initial entry of the map
   **/
  void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
  }

接下来看一下get方法。

源码如下。首先获取当前线程的ThreadLocalMap,然后,从ThreadLocalMap获取该ThreadLocal变量对应的value,然后返回value。如果ThreadLocalMap为null,则说明该线程还没有设置该ThreadLocal变量的值,那么就返回setInitialValue方法的返回值。其中的initialValue方法的返回值,通常情况下为null。但是,子类可以重写initialValue方法以返回期望的值。

  /**
   * Returns the value in the current thread's copy of this
   * thread-local variable. If the variable has no value for the
   * current thread, it is first initialized to the value returned
   * by an invocation of the {@link #initialValue} method.
   * @return the current thread's value of this thread-local
   **/
  public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
      ThreadLocalMap.Entry e = map.getEntry(this);
      if (e != null) {
        @SuppressWarnings("unchecked")
        T result = (T)e.value;
        return result;
      }
    }
    return setInitialValue();
  }
  /**
   * Variant of set() to establish initialValue. Used instead
   * of set() in case user has overridden the set() method.
   * @return the initial value
   **/
  private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
      map.set(this, value);
    else
      createMap(t, value);
    return value;
  }
  protected T initialValue() {
    return null;
  }

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI