zoukankan      html  css  js  c++  java
  • ThreadLocal

    ThreadLocal:是线程的本地变量,也叫本地存储。ThreadLocal为变量在每个线程中创建了一个副本,那么每个线程可以访问自己内容的副本变量

    ThreadLocal应用场景:

      比如

    class ConnectionManager {
         
        private static Connection connect = null;
         
        public static Connection openConnection() {
            if(connect == null){
                connect = DriverManager.getConnection();
            }
            return connect;
        }
         
        public static void closeConnection() {
            if(connect!=null)
                connect.close();
        }
    }

    这是一个数据库连接管理类,当多个线程同时调用的时候就会出现线程安全问题。

    避免线程安全问题:

      可以在方法前加synchronized修饰,但是这样就会大大影响了效率

    这里的这个例子可以不用connection共享,那么可能有的人就会想到,既然不用共享,那么我直接每个线程new一个出来。虽然这样是不会造成线程安全问题,而且线程与线程之间不必相互等待了,但是这样可能会导致服务器压力太大,因为每个线程都有一个connection,会严重影响性能

    class ConnectionManager {
         
        private  Connection connect = null;
         
        public Connection openConnection() {
            if(connect == null){
                connect = DriverManager.getConnection();
            }
            return connect;
        }
         
        public void closeConnection() {
            if(connect!=null)
                connect.close();
        }
    }
     
     
    class Dao{
        public void insert() {
            ConnectionManager connectionManager = new ConnectionManager();
            Connection connection = connectionManager.openConnection();
             
            //使用connection进行操作
             
            connectionManager.closeConnection();
        }
    }

    那么出现这种情况我们可以用ThreadLocal类

    ThreadLocal 提供了四个方法

    public T get() { }
    public void set(T value) { }
    public void remove() { }
    protected T initialValue() { }

    get()获取ThreadLocal在当前线程中保存的变量副本

    set()用来设置当前线程中变量副本

    remove()用来移除当前线程变量副本

    initialValue()是一个protected方法,一般用来重写,是一个延迟加载方法

     /**
         * 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)
                    return (T)e.value;
            }
            return setInitialValue();
        }

    get方法会掉用getMap()方法,如果map不为空,然后会取到<key,value>键值对

      /**
             * Get the entry associated with key.  This method
             * itself handles only the fast path: a direct hit of existing
             * key. It otherwise relays to getEntryAfterMiss.  This is
             * designed to maximize performance for direct hits, in part
             * by making this method readily inlinable.
             *
             * @param  key the thread local object
             * @return the entry associated with key, or null if no such
             */
            private Entry getEntry(ThreadLocal key) {
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
                if (e != null && e.get() == key)
                    return e;
                else
                    return getEntryAfterMiss(key, i, e);
            }

    注意这里取键值对传的是this,而不是当前线程t。

    如果map为空则调用setInit方法返回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;
        }

    getMap方法返回的是threadLocals,而threadlocals实际上就是一个ThreadLocalMap对象

     /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;

    ThreadLocalMap内部实现

    static class ThreadLocalMap {
    
            /**
             * 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;
                }
            }

    实际上是取的ThreadLocal作为键值

    set方法

     /**
         * 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;
        }

    当map不为空,则设置键值,否则创建map

    /**
         * 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
         * @param map the map to store.
         */
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
      * Construct a new map initially containing (firstKey, firstValue).
             * ThreadLocalMaps are constructed lazily, so we only create
             * one when we have at least one entry to put in it.
             */
            ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
                table = new Entry[INITIAL_CAPACITY];
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                table[i] = new Entry(firstKey, firstValue);
                size = 1;
                setThreshold(INITIAL_CAPACITY);
            }

    每个线程内部有一个ThreadLocalMap的成员变量threadLocals,这个变量实际上是来存储实际变量副本的,键值为当前ThreadLocal、变量,value为变量副本

    初始时,threadLocals为空,会通过get(),set()方法对threadLocals初始化,并且以当前线程变量为键值进行赋值

    在当前线程里面,如果要使用本地变量,就可以通过get方法在threadLocals里面查找

    public class Test {
        ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
        ThreadLocal<String> stringLocal = new ThreadLocal<String>();
     
         
        public void set() {
            longLocal.set(Thread.currentThread().getId());
            stringLocal.set(Thread.currentThread().getName());
        }
         
        public long getLong() {
            return longLocal.get();
        }
         
        public String getString() {
            return stringLocal.get();
        }
         
        public static void main(String[] args) throws InterruptedException {
            final Test test = new Test();
             
             
            test.set();
            System.out.println(test.getLong());
            System.out.println(test.getString());
         
             
            Thread thread1 = new Thread(){
                public void run() {
                    test.set();
                    System.out.println(test.getLong());
                    System.out.println(test.getString());
                };
            };
            thread1.start();
            thread1.join();
             
            System.out.println(test.getLong());
            System.out.println(test.getString());
        }
    }

    输出结果

    1

    main

    2

    thread0

    1

    main

    所以可以看出,threadLocal存的是线程的本地变量,main线程,和thread1线程存取的值都不同

  • 相关阅读:
    Problem C: 时间类的常量
    Problem B: 时间类的错误数据处理
    Problem A: 时间类的拷贝和整体读写
    Problem B: 平面上的点——Point类 (IV)
    Problem C: 平面上的点——Point类 (V)
    Problem A: 平面上的点——Point类 (III)
    中间的数(若已经排好序)
    软件工程概论团队结组
    软件工程个人作业04 子数组循环数组
    软件工程个人作业03
  • 原文地址:https://www.cnblogs.com/zmblog/p/8709469.html
Copyright © 2011-2022 走看看