ThreadLocal实现原理和应用场景详解 附代码

ThreadLocal不能解决多线程同步问题,它提供线程内部的局部变量,在多线程下,这个线程里面的变量不受其他线程的影响。
应用场景:
是在多线程访问多实例(每个线程对应一个实例)的时候,比如说访问网站。服务器会为每个用户开辟一个线程,线程中会存放每个用户的信息,每个线程之间是没有联系的,并且一个用户对应一个线程。在每次跳转时,会读取用户的信息,显示用户的信息,在这种情况下,可以把用户对象放在ThreadLocal中,方便读取操作。
一、早期JDK版本
以web场景举例,早期的ThreadLocal是这样的。有一个表示用户登录信息的HostHolder类。里面维护一个即ThreadLocal的实例类,它是private static修饰的,是类成员变量,它保存的对象是用户类User的实例。要让每个线程都保存一个用户的话,我们想到的方法是ThreadLocal类中维护一个map,key值就是每个线程的id,value值就是User的实例对象。这样在每个线程访问的时候就能保证只访问自己线程对应的value值。
这样map会保存所有线程的值。

public class HostHolder {
    private static ThreadLocal<User> users = new ThreadLocal<User>();

    public User getUser() {
        return users.get();
    }

    public void setUser(User user) {
        users.set(user);
    }

    public void clear() {
        users.remove();
    }
}

二、JDK1.8中ThreadLocal是这样实现的

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
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;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

Thread类中包含一个成员变量

ThreadLocal.ThreadLocalMap threadLocals = null;

这段摘自该博客http://blog.csdn.net/winwill2012/article/details/71625570

1首先获取当前线程
2根据当前线程获取一个Map
3如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的value e,否则转到5
4如果e不为null,则返回e.value,否则转到5
5Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map

优势:
这样设计之后每个Map的Entry数量变小了:之前是Thread的数量,现在是ThreadLocal的数量,能提高性能,据说性能的提升不是一点两点(没有亲测)
当Thread销毁之后对应的ThreadLocalMap也就随之销毁了,能减少内存使用量。

ThreadLocalMap是ThreadLocal的静态内部类,相当于哈希表。
这和早期的版本有明显不同,早期版本是在ThreadLocal类中维护一个map,key是线程id,根据线程的不同获取线程对应的value值。
改进后的ThreadLocal中没有map了,map放在每个Thread类中,即threadLocals,key值变成了this,也就是每个ThreadLocal类对象,value是线程中的维护的局部变量。比如如下的例子:

public class TestThreadLocal {
    private static final ThreadLocal<Integer> value = new ThreadLocal<>();
//    private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
//        @Override
//        protected Integer initialValue() {
//            return 0;
//        }
//    };
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new MyThread(i)).start();
        }
    }

    static class MyThread implements Runnable {
        private int index;

        public MyThread(int index) {
            this.index = index;
        }

        public void run() {
            value.set(2);
            System.out.println("线程" + index + "的初始value:" + value.get());
            for (int i = 0; i < 5; i++) {  //在各自线程中累加0~5
                value.set(value.get() + i);
            }
            System.out.println("线程" + index + "的累加value:" + value.get());
        }
    }
}

每个线程维护一个局部整型变量value,在每个线程中对这个变量进行累加,输出的结果不会相互影响。
输出:

线程0的初始value:2
线程4的初始value:2
线程4的累加value:12
线程3的初始value:2
线程1的初始value:2
线程2的初始value:2
线程1的累加value:12
线程3的累加value:12
线程0的累加value:12
线程2的累加value:12

如果是早期的版本,ThreadLocal类是泛型,里面只能存放整型变量。ThreadLocal的value中维护有一个map,线程1到5是map 的key值,具体的value值就是我设置的初始值2。

value.set(2);

然后各自线程维护各自的值,进行累加操作。

如果是新版的需要这么理解:在每个线程中,有一个map哈希表,key值是ThreadLocal的实例对象,上面代码中就是value,上面代码中map只有一个键值对。每个读取就在线程中map读取。

这样的好处据说可以提高效率,线程结束的时候能保证map也被回收。节省内存空间。

参考:
http://blog.csdn.net/winwill2012/article/details/71625570

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值