多线程之ThreadLocal分析

一、前言

最近在项目中用到ThreadLocal,所以在此做个对ThreadLocal的分析总结。

二、ThreadLocal是什么

ThreadLocal看上去是一个线程,其实它并不是一个Thread,而是一个threadlocalvariable,即线程局部变量,它的功能很简单,就是为每一个使用该变量的线程提供一个副本,使得每一个线程都可以独立的改变自己的副本,而不用担心影响其他线程所对应的副本。

三、ThreadLocal类的方法

ThreadLocal的主要方法有四个,分别如下:
1、public T get();
该方法的作用是获取当前线程的线程局部变量值。

2、protected T initialValue();
该方法是protected作用域的,是为了方便子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(T)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

3、public void set(T value);
该方法是设置当前线程的线程局部变量值。

4、public void remove();
该方法用于移除当前线程的线程局部变量,目的是减少内存占用。其实,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

四、线程隔离的实现

多线程,意味着对某个线程的操作不能影响其他线程,那么,ThreadLocal是如何实现为每一个线程单独维护线程局部变量的呢?
ThreadLocal中有一个静态类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;
            }
        }

上述代码中的Entry,是专门存放线程及其线程局部变量的,以各个线程ThreadLocal为键,对应的各线程局部变量为值存放到map中。
那么,每个线程获取各自的线程局部变量时,只需以当前ThreaLocal为键,就可以获得当前线程的局部变量。

误区

刚开始理解threadLocal的时候,在网上看到几篇文章都说,ThreadLocal能解决共享对象的并发访问问题。其实这是一个误区。
以下内容为通过引用论坛文章
正确理解ThreadLocal
来帮助理解:
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。

总结一下,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。
归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

猜你喜欢

转载自blog.csdn.net/u010398838/article/details/80163525