ThreadLocal使用以及原理

介绍

  • ThreadLocal是一个用于创建线程局部变量的类。当前线程通过ThreadLocal的set()方法设置的变量只对当前线程可见,通过get()获取设置的变量。

使用

  • 支持泛型
ThreadLocal<String> threadLocal = new ThreadLocal<>();
  • 当前线程通过ThreadLocal对象的set(value)/get()设置变量和获取设置的变量
threadLocal.set("jinshuai");
threadLocal.get();

原理

  • 每个线程Thread维护一个ThreadLocalMap<key,value>

    • key是ThreadLocal对象,value是通过set(value)设置的值
  • 当前线程调用threadLocal.set("jinshuai");
    • 首先获取当前线程所维护的ThreadLocalMap
    • 然后判断当前线程是否已经创建过这个ThreadLocalMap
      • 如果已经创建,会将ThreadLocal对象当作key,和当前线程要设置的值当作value放到ThreadLocalMap。
      • 如果没有创建,会创建并初始化一个ThreadLocalMap(类似HashMap 初始化数组长度为2的幂,设置扩充阈值...)然后同上↑
          public void set(T value) {
              // 获取当前线程对象
              Thread t = Thread.currentThread();
              // 获取线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null)
                  map.set(this, value);
              else
                  createMap(t, value);
          }
          // 获取ThreadLocalMap
          ThreadLocalMap getMap(Thread t) {
              return t.threadLocals;
          }
  • 当前线程调用threadLocal.get();
    • 首先获取当前线程所维护的ThereadLocalMap
    • 然后将ThreadLocal对象作为key获取对应的Entry
      • 如果Entry不为空获取Entry的value
      • 如果Entry为空直接返回一个setInitvalue()值也就是null
          public T get() {
              // 获取当前线程对象
              Thread t = Thread.currentThread();
              // 获取当前线程对应的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  // 将ThreadLocal(this)作为key获取entry
                  ThreadLocalMap.Entry e = map.getEntry(this);
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      // 获取entry的value
                      T result = (T)e.value;
                      return result;
                  }
              }
              // 如果entry为空
              return setInitialValue();
          }

应用场景

  • 如果一个对象非线程安全,但又不想通过加锁的方式实现线程安全,可以通过ThreadLocal.set()对象的值,比如SimpleDataFormat不是线程安全的,此时可以每个线程设置一个SimpleDataFormat对象
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
  • 当某一个变量比如User对象在多个方法中传递时,会变得比较乱此时可以通过ThreadLocal设置变量

    • 比如在Servlet中:

      doGet(HttpServletRequest req, HttpServletResponse resp) {
          User user = getLoggedInUser(req);
          doSomething(user)
          doSomethingElse(user)
          renderResponse(resp,user)
      }
    • 每个方法都需要一个user,不够优雅(咳咳...),此时可以通过设置一个ThreadLocal单例,然后set(user):
      doGet(HttpServletRequest req, HttpServletResponse resp) {
          User user = getLoggedInUser(req);
          ThreadLocalSingleInstace.getThreadLocal().set(user)
          try {
              doSomething()
              doSomethingElse()
              renderResponse(resp)
          }
          finally {
              ThreadLocalSingleInstace.getThreadLocal().remove()
          }
      }
      // 获取ThreadLocal单例
      class ThreadLocalSingleInstace {
          static private ThreadLocal threadLocal = new ThreadLocal<User>();
          static ThreadLocal<User> getThreadLocal() {
              return threadLocal;
          }
      }

注意

  • 用完以后应该调用remove()移除设定的值,防止内存泄漏
// 会移除这个Entry
threadLocal.remove();
  • 在线程池中由于线程会被复用,所以不会停止,导致每个线程的ThreadLocalMap里的key是ThreadLocal的弱引用,GC时会将其回收,但是其对应的value一直有一个强引用不会被回收造成内存泄漏。
       /**
        * The entries in this hash map extend WeakReference, using
        * its main ref field as the key (which is always a
        * ThreadLocal object).  Note that null keys (i.e. entry.get()
        * == null) mean that the key is no longer referenced, so the
        * entry can be expunged from table.  Such entries are referred to
        * as "stale entries" in the code that follows.
        */
       static class Entry extends WeakReference<ThreadLocal<?>> {
           /** The value associated with this ThreadLocal. */
           Object value;

           Entry(ThreadLocal<?> k, Object v) {
               super(k);
               value = v;
           }
       }

参考

  • https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/
  • http://blog.xiaohansong.com/2016/08/06/ThreadLocal-memory-leak/#
  • https://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable
  • https://stackoverflow.com/questions/1490919/purpose-of-threadlocal

原文地址:https://www.cnblogs.com/jinshuai86/p/9270705.html

时间: 2024-10-16 13:24:23

ThreadLocal使用以及原理的相关文章

Android的消息机制之ThreadLocal的工作原理

提到消息机制大家应该都不陌生,在日常开发中不可避免地要涉及到这方面的内容.从开发的角度来说,Handler是Android消息机制的上层接口,这使得开发过程中只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行.很多人认为Handler的作用是更新UI,这说的的确没错,但是更新UI仅仅是Handler的一个特殊的使用场景,具体来说是这样的:有时候需要在子线程中进行耗时的IO操作,这可能是读取文件或者访问网络等,当耗时操作

【原创】源码角度分析Android的消息机制系列(三)——ThreadLocal的工作原理

ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Android源码(API24)中对ThreadLocal的定义: public class ThreadLocal<T> 即ThreadLoca是一个泛型类,再看对该类的注释: /** * This class provides thread-local variables. These variables differ from * their normal counterparts in that each thread th

ThreadLocal使用和原理简析

1. 解决共享资源冲突 对于并发工作,需要某种方式来防止两个任务同时访问相同的资源,至少在关键阶段不能出现这种冲突情况. 方法之一就是当资源被一个任务使用时,在其上加锁.第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁之时,另一个任务就可以锁定并使用它,以此类推.Java中的synchronized.ReentrantLock就属于这种方式,关于这部分,前面有专门撰文详述: synchronized学习.synchronized底层实现学习 Ree

ThreadLocal简介与原理

一.引入场景 1. 打印方法执行的耗时 public void service(){ before(); doSomething(); after(); } 2. 在before和after记录当前时间,两者相减得到doSomething()的耗时 private long startTime; // 定义变量开始时间 public void before(){ startTime=System.CurrentTimeMills(); // 记录开始时间 } public void doSome

ThreadLocal原理及使用示例

简介:本文已一个简要的代码示例介绍ThreadLocal类的基本使用方式,在此基础上结合图片阐述它的内部工作原理. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ThreadLocal<T> 简介和使用示例 ThreadLocal只有一个无参的构造方法 public ThreadLocal() ThreadLocal的相关方法 public T get() public void set(T value) public v

ThreadLocal的设计与使用(原理篇)

在jdk1.2推出时开始支持java.lang.ThreadLocal.在J2SE5.0中的声明为: public class ThreadLocal<T> extends Object ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量).也许把它命名为ThreadLocalVar更加合适.线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量

ThreadLocal 工作原理、部分源码分析

1.大概去哪里看 ThreadLocal 其根本实现方法,是在Thread里面,有一个ThreadLocal.ThreadLocalMap属性 ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocalMap 静态内部类维护了一个Entry 数组 private Entry[] table; 查看Entry 源码,它维护了两个属性,ThreadLocal 对象 与一个Object static class Entry extends W

在spring+beranate中多数据源中使用 ThreadLocal ,总结的原理 --费元星

设计模式 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.通过ThreadLocal.set()将

ThreadLocal原理

什么是ThreadLocal 该类提供了线程局部变量,就是为每一个使用它的线程提供一个变量的副本,使每一个 线程可以独立的改变自己的副本,而不会和其它线程发生冲突.说白了就是解决对线程访问共享 资源时发生冲突的问题,也算是一种同步的方式. ThreadLocal的实现原理 public class ThreadLocal   {   private Map values = Collections.synchronizedMap(new HashMap());   public Object g