zoukankan      html  css  js  c++  java
  • ThreadLocal 简述

    ThreadLocal的理解

    Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量。因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的ThreadLocal变量。
    The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to a ThreadLocal variable, then the two threads cannot see each other's ThreadLocal variables.

    解析ThreadLocal类

    • ThreadLocal中 get()方法的实现
    /**
         * 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();
        }
    

    判断当前线程,通过getMap 方法返回一个ThreadLocalMap 。
    判断map 是否为null ,如果存在的话,就从ThreadLocalMap中获取对应的value,不存在调用setInitialValue方法。
    来看看getMap方法的实现。

        /**
         * 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是什么?

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

    再来看看 ThreadLocalMap ,通过map来实现key-value 的存储。

    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;
                }
            }
    
            /**
             * Set the value associated with key.
             *
             * @param key the thread local object
             * @param value the value to be set
             */
            private void set(ThreadLocal key, Object value) {
    
                // We don't use a fast path as with get() because it is at
                // least as common to use set() to create new entries as
                // it is to replace existing ones, in which case, a fast
                // path would fail more often than not.
    
                Entry[] tab = table;
                int len = tab.length;
                int i = key.threadLocalHashCode & (len-1);
    
                for (Entry e = tab[i];
    		 e != null;
    		 e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal k = e.get();
    
                    if (k == key) {
                        e.value = value;
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    
    • ThreadLocal中set()方法的实现
     /**
         * 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方法对应起来看。这里也是get来进行判断。

    代码测试

    
    public class ThreadLocalTest implements Runnable {
    
    	private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
    
    	@Override
    	public void run() {
    		threadLocal.set((int) (Math.random() * 100D));
    
    		try {
    			Thread.sleep(2000);
    		} catch (InterruptedException e) {
    		}
    
    		System.out.println(threadLocal.get());
    	}
    
    	public static void main(String[] args) throws Exception {
    		ThreadLocalTest sharedRunnableInstance = new ThreadLocalTest();
    
    		Thread thread1 = new Thread(sharedRunnableInstance);
    		Thread thread2 = new Thread(sharedRunnableInstance);
    
    		thread1.start();
    		thread2.start();
    
    		thread1.join(); // wait for thread 1 to terminate
    		thread2.join(); // wait for thread 2 to terminate
    	}
    }
    

    总结

    上面的例子,将ThreadLocalTest 实例传递給两个线程,执行run 方法这两个线程分别保存了不同的数据。如果不是访问ThreadLocal对象,第2个线程会覆盖掉第1个线程的值。ThreadLocal不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

    ThreadLocal使用场景,多线程实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。 ThreadLocal 也常用数据库连接操作。
    参考

  • 相关阅读:
    Eclipse怎么导入外来项目
    java批量解压文件夹下的所有压缩文件(.rar、.zip、.gz、.tar.gz)
    java批量读取多个文件并存入数据库
    字符串转日期,出现错误
    tomcat设置默认欢迎页、server.xml配置文件中的标签理解
    个人网站搭建时linux中的相关配置记录(mysql,jdk,nginx,redis)
    linux定时重启tomcat服务的脚本学习
    linux服务器的相关信息查看(端口占用,cpu、内存占用,防火墙,系统信息,vim编辑器使用等)
    java极光推送记录
    @transactional注解,报错后数据库操作回滚失败
  • 原文地址:https://www.cnblogs.com/tonyY/p/4930142.html
Copyright © 2011-2022 走看看