美文网首页
九、Java高级特性(ThreadLocal的使用和原理分析)

九、Java高级特性(ThreadLocal的使用和原理分析)

作者: 大虾啊啊啊 | 来源:发表于2021-05-30 20:50 被阅读0次

一、前言

我们在前面学习知道对象和变量的并发访问我们可以使用synchronized、volatile关键字。synchronized保证了数据的原子性,解决并发访问同一数据的时候排队执行。volatile保证了数据的可见性,使得在多线程共享一个数据的时候,一旦数据发生改变。各个线程都能拿到最新的数据的之。
而ThreadLocal的出现使得每个线程都有自己的一份副本,也就是使得每个线程都单独拥有自己的数据。达到线程之间的数据隔离。

二、ThreadLocal的使用

package com.it.test.thread;

public class ThreadLocalDemo {
    static ThreadLocal<Integer> local1 = new ThreadLocal<>() {
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    public static void main(String[] args) {
        new Thread1().start();
        new Thread2().start();
    }

   static class Thread1 extends Thread {
        @Override
        public void run() {

            local1.set(1);
            System.out.println(Thread.currentThread().getName()+":"+local1.get());

        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            local1.set(2);
            System.out.println(Thread.currentThread().getName()+":"+local1.get());
        }
    }
}

Thread-0:1
Thread-1:2

三、ThreadLocal源码分析

1、set方法

获取当前线程,拿到当前线程的ThreadLocalMap ,如果map为空,则创建一个map,否则之间给当前map设置值,key为当前ThreacLocal对象,值就是我们存放的值。

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

来看下createMap方法,为当前线程设置了一个ThreadLocalMap对象,

 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

来看下ThreadLocalMap的源码,包含了一个静态内部类Entry,继承了弱引用的ThreadLocal。

   static class ThreadLocalMap {

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

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

在上面为线程创建一个ThreadLocalMap对象的时候,调用了ThreadLocalMap的构造方法,在ThreadLocalMap的构造方法,构造方法传入了:
ThreadLocal<?> firstKey, Object firstValue两个参数。
接着内部创建了内部类的Entry类型数组,
table = new Entry[INITIAL_CAPACITY];
然后创建Entry对象,传入firstKey和firstValue,存入到Entry数组中。
这里需要注意的是,之所以创建了一个Entry数组,是因为一个线程可能有多个ThreadLocal对象,
每一个ThreadLocal,都会在ThreadLocalMap内部的Entry数组存入。

   ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

也就是当我们在某个线程里通过ThreadLocal设置值的时候,那么就会在当前线程中创建一个ThreadLocalMap,如果存在ThreadLocalMap则直接获取当前线程的ThreadLocalMap。而ThreadLocalMap包含一个Entry数组,Entry有两个属性key和value。使用ThreadLocal设置值的时候,就会把ThreadLocal对象作为key,以及具体设置的值,初始化给Entry对象,然后存入Entry类型的数组中。当有多个
ThreadLocal的时候,数组就会存入多个Entry对象。

2、get方法

   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 。调用ThreadLocalMap 的getEntry方法。

   private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.refersTo(key))
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }

我们知道ThreadLocalMap 中的包含了一个Entry数组,getEntry方法,传入了ThreadLocal对象作为key,然后经过系列的位运算,算出数组的下标。最好从数组中拿到Entry对象,

   ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }

拿到Entry对象,直接获取Entry对象里的value值,就是我们存入的value值。

小结

  • ThreadLocal使得每个线程拥有自己的一份副本,也就是拥有自己的一份数据。达到线程直接数据隔离
  • ThreadLocal的实现原理的核心就是:
    (1)当我们在某个线程下第一次使用ThreadLocal设置值的时候,那么就会为这个线程创建一个ThreadLocalMap 对象,ThreadLocalMap 中包含了一个Entry数组。Entry中包含了两个属性key和value。
    在设置值的时候,就会将我们的值存放到ThreadLocalMap 的Entry数组中,其中key为当前ThreadLocal对象,值就是我们设置的值。
    (2)当我们使用ThreadLocal获取值的时候,首先是会获取当前线程的ThreadLocalMap ,然后获取ThreadLocalMap 中的数组。接着通过当前ThreadLocal作为key,通过位运算获取到属性key为当前ThreadLocal对象的Entry对象,在获取Entry中的value属性。就是我们需要获取的值。
image.png

相关文章

网友评论

      本文标题:九、Java高级特性(ThreadLocal的使用和原理分析)

      本文链接:https://www.haomeiwen.com/subject/ejxdsltx.html