ThreadLocal底层

1. 首先我们来看一下他的使用

public class ThreadLocalTest {

    public static void main(String[] args) {
        
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        thread1.start();
        thread2.start();
    }
}
class MyThread extends Thread{
    //泛型类型也就是key值的类型
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    
    @Override
    public void run() {
        //生成一个四位的随机字符串
        String id = UUID.randomUUID().toString().substring(0,4);
        System.out.println(Thread.currentThread().getName()+"对应的数字为"+id);
        //保存到threadLocal中
        threadLocal.set(id);
        
        for (int i = 0; i < 20; i++) {
            //threadLocal.get()从中取出
            System.out.println(Thread.currentThread().getName()+"的数据"+threadLocal.get());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我们观察打印输出:可以发现,每一条线程对应的数据是固定的,这个数据是每个线程独有的,这就是ThreadLocal

Thread-0对应的数字为ac58
Thread-1对应的数字为fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2
Thread-1的数据fec2
Thread-0的数据ac58
Thread-0的数据ac58
Thread-1的数据fec2
Thread-0的数据ac58
Thread-1的数据fec2

2. 接下来我们来分析它的源码实现

1. 首先观察构造器,发现什么也没有

2. 然后我们再来看刚才调用的方法threadLocal.set(id);

public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //传入当前线程,返回一个ThreadLocalMap,那么这个map是什么东西,我们再进去看一下
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

2. 1--getMap(t)

ThreadLocalMap getMap(Thread t) {
    //直接返回了线程对象里面的一个变量,我们再来看看这个变量是什么东西
    return t.threadLocals;
}
2. 1--1--return t.threadLocals;

这次我们来到了Thread类中

      //我们可以看到这个是一个ThreadLocal里面的一个静态内部类
      ThreadLocal.ThreadLocalMap threadLocals = null;
2. 1--1--ThreadLocalMap

我们稍微观察一下就可以看到,这个的实现大致类似于HashMap
其中的"对"的key值时ThreadLocal对象,值时Object类型
Entry(ThreadLocal<?> k, Object v)
这时我们也可以看出来,ThreadLocal中key值并不是很多地方说的当前线程对象,而是ThreadLocal对象

static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
      ......
}

2. 1--我们接下来回到set方法中

      ThreadLocalMap map = getMap(t);//拿到map之后
        if (map != null)//如果为空,则map调用set
            //因为当前是ThreadLocal调用这个set方法,所以key值的this是当前ThreadLocal对象
            map.set(this, value);
        else//否则创建map,这个创建没什么好说的,就是new ThreadLocalMap
            createMap(t, value);

3. 在接下来就轮到我们的get方法了threadLocal.get()

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        //上面的两步也一样,获取ThreadLocalMap对象
        if (map != null) {
            //调用map的getEntry,看3.1
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                //将拿到的entry的value返回回去,也就是我们保存的值
                return result;
            }
        }
        return setInitialValue();
    }

3. 1--获取当前的entry

        private Entry getEntry(ThreadLocal<?> key) {
            //计算当前的ThreadLocal对应的位置,详情可以去了解HashMap
            int i = key.threadLocalHashCode & (table.length - 1);
            //拿到对应的entry
            Entry e = table[i];
            if (e != null && e.get() == key)
                //返回entry
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

3. 最后我们也可以自己实现一个简单的ThreadLocal

public class ThreadLocalUtils {

    //key值是当前线程的id
    private Map<Long, Object> map = new HashMap<>();

    public Object get() {
        long id = Thread.currentThread().getId();
        return map.get(id);
    }

    public void set(Object value) {
        long id = Thread.currentThread().getId();
        //将旧的引用拿到,换成新值
        Object o = map.get(id);
        o = value;
    }
}

使用

public class MyThreadLocalTest {

    public static void main(String[] args) {
        
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        thread1.start();
        thread2.start();
    }
    
    
}

class MyMyThread extends Thread{

    private static ThreadLocalUtils threadLocal= new ThreadLocalUtils();
    
    @Override
    public void run() {
        
        String id = UUID.randomUUID().toString().substring(0,4);
        System.out.println(Thread.currentThread().getName()+"对应的数字为"+id);
        threadLocal.set(id);
        
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"的数据"+threadLocal.get());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
posted @ 2020-09-16 14:56  微花  阅读(232)  评论(0编辑  收藏  举报

Loading