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

    一:ThreadLocal的原理

    1.说明

      ThreadLocal从字面意思来理解,是一个线程本地变量,也可以叫线程本地变量存储。有时候一个对象的变量会被多个线程所访问,这个时候就会有线程安全问题,当然可以使用synchronized关键字来为该变量加锁,进行同步处理来限制只能有一个线程来使用该变量,但是这样会影响程序执行的效率,这时ThreadLocal就派上了用场;
      使用ThreadLocal维护变量的时候,会为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个当前变量。这样同时有多个线程访问该变量并不会相互影响,因为他们都是使用各自线程存储的变量,所以不会存在线程安全的问题。
      同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式,前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问且互不影响。

    2.源码

      

      ThreadLocal实现原理:每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。

      也就是说ThreadLocal本身不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

      值得注意的是图中(图片摘自网络)的虚线,表示ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。

    3.ThreadLocal中有四个主要的方法

      public T get() {} //获取当前线程中ThreadLocal副本
      public void set(T value) {} //用来设置当前线程中ThreadLocal副本
      public void remove() {} //移除当前线程中ThreadLocal副本
      protected T initialValue() {} //是一个protected方法,一般使用时需要重写,默认返回为null

    4.get方法的处理逻辑

    public T get() {
            //获取当前线程
            Thread t = Thread.currentThread();
            //获取当前线程中的ThreadLocalMap,每个线程都会有一个类型为ThreadLocalMap的inheritableThreadLocals变量
            ThreadLocalMap map = getMap(t);
            //如果当前线程中ThreadLocalMap为null,则返回初始化值
            if (map != null) {
                //获取map中ThreadLocal副本
                ThreadLocalMap.Entry e = map.getEntry(this);
                //获取存储的变量值
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            //返回初始化值,一般setInitialValue()需要重写,自定义初始化值
            return setInitialValue();
        }
     

      Entry:

      Entry类是内部类ThreadLocalMap中的内部类,集成了WeakReference类,如下所示:

    static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                //与此ThreadLocal关联的值
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }

      此hash map的entries使用它的“主引用”字段作为键(它始终是ThreadLocal对象)继承自WeakReference类。 请注意,null键(即entry.get()== null)表示不再引用该键,因此可以从table中删除该entry。 这些entries在下面的代码中称为“陈旧entry”。

      既然Entry继承了WeakReference,说明ThreadLocal使用了弱引用,如果entry.get()==null,当前ThreadLocal副本会立即被GC掉,避免高并发情况下出现内存溢出。

    5.set逻辑

    public void set(T value) {
            //获取当前线程
            Thread t = Thread.currentThread();
            //从当前线程中的ThreadLocalMap中获取存储的当前副本的值
            ThreadLocalMap map = getMap(t);
            //如果map不为空,覆盖当前副本中的值,否则新建副本
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }

       将此线程局部变量的当前线程副本设置为指定值。 大多数子类都不需要重写此方法,仅依靠initialValue()方法来设置线程局部变量值。

    6.remove逻辑

       public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }
    

      删除当前线程ThreadLocal的变量值。 如果当前线程随后通过该线程从get()方法中获取ThreadLocal的变量值,那么它的值将通过调用initialValue()方法重新获取初始值,除非它的值是当前线程临时调用set()方法重新设置了值。 这可能导致在当前线程中多次调用initialValue()方法。

    7.initialValue方法

    protected T initialValue() {
            return null;
        }
    

      这个方法默认返回是null,注意看一下它的修饰符是protected,一般情况这个方法是要使用者去实现的,设置默认的初始值。

    二:

    1.使用场景

      ThreadLocal常用于解决数据库连接、session管理等

  • 相关阅读:
    MYSQL57密码策略修改
    dataSource' defined in class path resource [org/springframework/boot/autocon
    ubuntu14.04 red5
    hadoop中修改端口号
    FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed for Block pool <registering> (Datanode Uuid unassigned) service to controller/192.168.1.183:9000. Exiting. java.io.IOExcep
    hadoop ssh 问题WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
    openstack安装dashboard后访问horizon出错 End of script output before headers: django.wsgi
    sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) Cannot add a NOT NULL column with default value NULL [SQL: u'ALTER TABLE address_scopes ADD COLUMN ip_version INTEGER NOT NULL']
    mysql ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 "No such file or directory")
    OpenStack安装keyston 错误BError: (pymysql.err.InternalError) (1071, u‘Specified key was too long; max key length is 767 bytes‘) [SQL: u‘ CREATE TABLE migrate_ver
  • 原文地址:https://www.cnblogs.com/juncaoit/p/11412504.html
Copyright © 2011-2022 走看看