ThreadLocal解析

本文详细介绍了ThreadLocal的概念及其在多线程环境中的作用,通过对比普通变量和ThreadLocal变量的使用,展示了如何避免线程间的数据冲突。同时,文章深入探讨了ThreadLocal的设计原理和内部实现机制,包括其如何利用ThreadLocalMap进行数据存储,以及解决hash冲突的方法。

概念

ThreadLocal称之为线程本地变量。在多线程的情况下每一个线程保存一份共享变量的副本,那么访问的时候只会访问自己保存的那个副本,而不会影响其他线程的操作。

使用实例

下面的代码创建5个线程,然后给成员变量每次加5.

public class ThreadLocalMini {

    private static Integer num=0;

    public static void main(String[] args) {
        Thread[] threads=new Thread[5];
        for (int i=0;i<threads.length;i++){
            threads[i]=new Thread(()->{
                num+=5;
                System.out.println(Thread.currentThread().getName()+":"+num);
            },"Thread-"+i);
        }

        for (int i=0;i<threads.length;i++){
            threads[i].start();
        }
    }
}

打印结果:

Thread-0:5
Thread-1:10
Thread-2:15
Thread-3:20
Thread-4:25

但是我希望每一个线程打印的都是5,不要在原来的基础上加5.可以使用ThreadLocal去改造。

public class ThreadLocalMini {

    private static ThreadLocal<Integer> num2=new ThreadLocal(){
        /**初始化*/
        @Override
        protected Object initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) {
        Thread[] threads=new Thread[5];
        for (int i=0;i<threads.length;i++){
            threads[i]=new Thread(()->{
                int res = num2.get().intValue();
                res+=5;
                num2.set(res);
                System.out.println(Thread.currentThread().getName()+":"+num2.get());
            },"Thread-"+i);
        }

        for (int i=0;i<threads.length;i++){
            threads[i].start();
        }
    }
}

打印结果如下:

Thread-0:5
Thread-3:5
Thread-4:5
Thread-2:5
Thread-1:5

设计原理

从概念以及使用原理上来看,我们必须保证每个线程之间互相不影响。即线程隔离,其次每个线程还要对应保存数据。那么我们可以使用ConcurrentHashMap来做一个简单的实现。

/**
 * @Author: gosaint
 * @Description:
 * @Date Created in 19:31 2020/4/17
 * @Modified By:
 *
 * 我们实现4个方法 put(Object obj),get(),remove(),init()方法
 */
public class SimpleThreadLocal {
    /**
     * map存储的key为当前线程,value为存储的变量
     */
    private static ConcurrentHashMap<Thread,Object> map=new ConcurrentHashMap<>();

    public void put(Object obj){
        map.put(Thread.currentThread(),obj);
    }

    public Object get(){
        Thread thread = Thread.currentThread();
        Object o = map.get(thread);
        if(o==null && !map.containsKey(thread)){
            o=initialValue();
            map.put(thread,o);
        }
        return o;
    }

    public void remove(){
        map.remove(Thread.currentThread());
    }

    protected Object initialValue() {
        return null;
    }

}

set源码

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

这里和我设想方案有点差别。它使用了ThreadLocalMap来保存数据,我们可以看下ThreadLocalMap的内部结构。

static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

本质上还是一个key-value形式的Map数据结构。
在这里插入图片描述
在这里需要说明的是ThreadLocalMap也是类似于HashMap一样的数据结构。

 private final int threadLocalHashCode = nextHashCode();

 private static AtomicInteger nextHashCode =
        new AtomicInteger();

  
 private static final int HASH_INCREMENT = 0x61c88647;

  
 private static int nextHashCode() {
   return nextHashCode.getAndAdd(HASH_INCREMENT);
 }

上面的几个属性主要是确认key在hash表中的位置,ThreadLocalMap使用的是线性探测法解决hash冲突,每次在原来的基础上加0x61c88647这个数字去解决的。而且nextHashCode 这个变量也是原子操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值