ThreadLocal解析

ThreadLocal

java1.5API定义:该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 getset 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

当摸个对象需要被多个线程访问的时候,会出现线程安全问题。这时使用synchronized关键字加锁,只能有一个线程来使用此变量,但是加锁会影响执行效率。这时使用ThreadLocal来解决这个问题。

当使用ThreadLocal来维护变量时,为每一个使用该变量的线程都提供一个独立的变量副本,使得多个线程在访问变量的时候不会彼此影响。

查看源码,我们以set方法举例。

 public class Thread implements Runnable {
            ....
    ThreadLocal.ThreadLocalMap threadLocals = null;//ThreadLocal作为Thread的成员变量
        ....
 }
public class ThreadLocal<T> {
        ....
    public void set(T value) {
        Thread t = Thread.currentThread(); //获取当前线程
        ThreadLocalMap map = getMap(t); //获取当前线程的threadLocals变量
        if (map != null)
            map.set(this, value);//为threadLocals赋值,调用ThreadLocalMap中的set()方法
        else
            createMap(t, value);//创建新的ThreadLocalMap
    }
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
        ....
     ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
        ....
    static class ThreadLocalMap {
        ....
        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];//默认16长度的数组
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
        static class Entry extends WeakReference<ThreadLocal> {
            Object value;
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
         private void set(ThreadLocal key, Object value) {
            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();//返回此引用对象的指示对象
            //判断得到的ThreadLocal和当前的ThreadLocal是否一致
                if (k == key) { //一致
                    e.value = value; //赋值
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            //都不正确则重新实例化一个Entry
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
        ....
    }
}

从源码中看出Thread中有一个ThreadLocalMap类型的变量threadLocals,当ThreadLocal需要set数据时,会查看当前线程的threadLocals是否存在,如果存在就直接调用ThreadLocalMap类中的set()方法。ThreadLocalMap类中有一个Entry类(类中包含ThreadLocal类型的K和Object类型的value)和一个Entry[]数组类型的table,set()数据时,需要先查找当前的ThreadLocal,当前的ThreadLocal和传参数进来的ThreadLocal是否一致,如果一致就赋值。

ThreadLocal是如何为每一个线程创建一个变量副本的,下面举一个例子来看一看。例子来源于 博客http://www.cnblogs.com/dolphin0520/p/3920407.html

public class ThreadLocalTest {
    public static void main(String[] args) throws InterruptedException {
        final ThreadLocalTest test = new ThreadLocalTest();

        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
        // 在这里新建了一个线程
        Thread thread1 = new Thread() {
            public void run() {
                test.set(); // 当这里调用了set方法,进一步调用了ThreadLocal的set方法是,会将ThreadLocal变量存储到该线程的ThreadLocalMap类型的成员变量threadLocals中,注意的是这个threadLocals变量是Thread线程的一个变量,而不是ThreadLocal类的变量。
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();

        System.out.println(test.getLong());
        System.out.println(test.getString());
    }

    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();
    }

}

代码的输出结果:  1  main  9  Thread-0  1  main

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值