ThreadLocal的理解与使用

来源:互联网 发布:法剧 知乎 编辑:程序博客网 时间:2024/05/21 08:52

首先来看一下多线程问题:

class Test{    private static Integer data = 0;//使用Integer普通变量保存数据    public static void set(Integer num){        data = num;    }    public static Integer get(){        System.out.println("num ==> " +data);        return data;    }}public class CopyOfMainTest{    public static void main(String[] args){        for(int i=0;i<10;i++){            final int num = i;            new Thread(){                public void run(){                    Test.set(num);                    Test.get();                }            }.start();        }    }}

执行的结果如下:

num ==> 4
num ==> 0
num ==> 4
num ==> 4
num ==> 3
num ==> 5
num ==> 7
num ==> 9
num ==> 9
num ==> 8

分析:可以看到以下多个线程中data的值时一样的,使得数据结果很混乱,按照正常的思维模式,每一个线程应该得到一个数据,从0到9,这就是我们经常说的线程安全问题,这里肯定是不安全的。当然了你可以用synchronized去解决这个问题,但是多线程的并发处理的效率就会大大折扣。


来看一个用ThreadLocal存放变量的例子:

//使用ThreadLocal保存数据class Test{    public static ThreadLocal<Integer> local = new ThreadLocal<Integer>();    public static void set(Integer num){        local.set(num);    }    public static Integer get(){        System.out.println("num ==> " + local.get());       return local.get();    }}public class CopyOfMainTest{    public static void main(String[] args){        for(int i=0;i<10;i++){            final int num = i;            new Thread(){                public void run(){                    Test.set(num);                    Test.get();                }            }.start();        }    }}

执行的结果如下:

num ==> 0
num ==> 1
num ==> 2
num ==> 3
num ==> 6
num ==> 5
num ==> 8
num ==> 4
num ==> 9
num ==> 7

只是顺序上变了而已,每个线程都得到自己应该得到的Integer变量值。


分析原因:

通过对java API的查阅,发现Tread 和ThreadLocal都在java.lang包下。在java.lang.Thread源码中,有这样一段代码。这个threadLocals就是那些设计者们已经为我们考虑了在线程中存放变量的问题。

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

 /**     * 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() {//get方法        Thread t = Thread.currentThread();//获取当前上下文线程(哪个线程在调用该方法)        ThreadLocalMap map = getMap(t);//把当前线程作为参数丢给getMap方法,返回一个ThreadLocal的内置对象ThreadLocalMap (map)        if (map != null) {              ThreadLocalMap.Entry e = map.getEntry(this);//取出当前上下ThreadLocal对象对应的值 Entry getEntry(ThreadLocal key) if (e != null) return (T)e.value; } return setInitialValue();       }/**     * 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) {//set方法        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);//把当前的ThreadLocal对象作为key把值存入,set(ThreadLocal key, Object value)        else            createMap(t, 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;    } 

通过对源码的分析发现,ThreadLocal在存变量的时候是根据当前上下文线程 和 当前ThreadLocal对象存入数值的。所以当调用get方法时,根据当前上下文线程和ThreadLocal对象就取出了自己唯一的那个值,而不会取到别的线程的值。


在实际开发中,在拦截器中判断当前用户登录成功以后,可以用ThreadLocal来存放用户信息。


强烈推荐两篇文章:

http://qiankunli.github.io/2014/09/02/ThreadLocal.html

http://woshixy.blog.51cto.com/5637578/1275284


注册没几天,就写了一篇,写的不好,请大家提出意见,慢慢改进。

1 0
原创粉丝点击