zoukankan      html  css  js  c++  java
  • ThreadLocal

    相关链接

      http://www.jianshu.com/p/a8fa72e708d3

      http://www.jasongj.com/java/threadlocal/

      https://juejin.cn/post/6893791473121280013#heading-9

    基本概念

    ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,
    它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

    适用场景

    ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

    误解⚠️:好多人错误的以为,ThreadLocal是为了解决多线程访问共享变量的问题,即线程间同步问题

    原理

        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);//创建当前线程的map(仅第一次调用)
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);//this代表的是ThreadLocal
        }
    
        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null)
                    return (T)e.value;
            }
            return setInitialValue();//null
        }
    
    
        private T setInitialValue() {
            T value = initialValue();//null
            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;
        }

    内存泄漏

         ThreadLocal为每个线程都提供了变量的副本,使得每个线程在某一时间访问到的并非同一个对象,这样就隔离了多个线程对数据的数据共享。

        

          我们的设计方案,ThreadLocal持有的Map会持有Thread对象的引用,这就意味着只要ThreadLocal对象存在,那么Map中的Thread对象就永远不会被回收。ThreadLocal的生命周期往往都比线程要长,所以这种设计方案很容易导致内存泄漏。

     

     

     

      创建Handler的时候,需要先创建当前线程的Looper,Android默认在启动的时候为我们创建的  主线程的Looper,所以我们可以在主线程直接创建Handler。

      但是在子线程创建Handler的时候,我们必须先手动创建Looper,才能创建Handler,否则会抛出一个必须调用Looper.prepare()的异常信息。

      原因:      

    public Handler(Callback callback, boolean async) {
      //省略
    
      mLooper = Looper.myLooper();
      if (mLooper == null) {
          throw new RuntimeException(
              "Can't create handler inside thread that has not called Looper.prepare()");
      }
      //省略
    }

      问题:

        相同的代码,为什么在主线程创建就可以,在子线程创建就异常???为什么在子线程中,通过Looper.myLooper()方法获取的就是为空呢?

      查看Looper.myLooper()源码,可知道原因:ThreadLocal获取的是“当前线程”的Looper

    sThreadLocal.get();

      ThreadLocal在什么时候set值的那?--- 在Looper中源码中

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

      ThreadLocal的初始化:--- 在Looper中源码中

        // sThreadLocal.get() will return null unless you've called prepare().
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

        看注释可以知道,sThreadLocal.get()之前,必须Looper.prepare(),否则get到的值为null。

    ThreadLocal是什么? 

        看一下官方的解释:

    Implements a thread-local storage, that is, a variable for which each thread
    has its own value. All threads share the same {@code ThreadLocal} object,
    but each sees a different value when accessing it, and changes made by one
    thread do not affect the other threads. The implementation supports
    {@code null} values.

        意思是说:ThreadLocal实现与线程相关的存储。所有线程共享一个ThreadLocal对象,但是不同的线程有自己的值。并且当一个线程的值发生改变之后,不会影响其他的线程的值。

          所以这就解释了为什么 在子线程中,如果不Looper.prepare()就直接创建Handler的时候,会抛出异常。

    ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据,,数据存储以后,只有在指定线程中可以获取到存储的数据。对于其它线程来说无法获取到数据。
    ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。

      所以今后,如果多线程的每一个线程都有一个相同变量,但是每个线程的变量值只有当前线程自己可以修改,不受其它线程的影响,那么你就可以用ThreadLocal。多线程共享同一个ThreadLocal对象。    

       

  • 相关阅读:
    【原创】go语言学习(五)函数详解1
    【原创】go语言学习(四)流程控制
    aws使用之负载均衡elb要点
    【转】只因写了一段爬虫,公司200多人被抓!
    【原创】go语言学习(三)字符串串、时间和日期类型
    【原创】go语言之打印目录
    【转】Sentry 入门实战
    【原创】go语言学习(二)数据类型、变量量、常量量
    【原创】导出aws ec2为csv
    (转)实验文档5:企业级kubernetes容器云自动化运维平台
  • 原文地址:https://www.cnblogs.com/lang-yu/p/6744155.html
Copyright © 2011-2022 走看看