ThreadLocal看其他的有点蒙,自己写一下。

前言

老样子还是贴源码,更方便。

官方解释:该类提供线程本地变量。 这些变量的不同之处在于,访问这些变量的每个线程都有自己的、独立初始化的变量副本。变量的副本实例通常是私有的静态字段。 

甚至他还举了个例子!如下。

// 官方源码
import java.util.concurrent.atomic.AtomicInteger;
 
  public class ThreadId {
      // Atomic integer containing the next thread ID to be assigned
      private static final AtomicInteger nextId = new AtomicInteger(0);
 
      // Thread local variable containing each thread's ID
      private static final ThreadLocal<Integer> threadId =
          new ThreadLocal<Integer>() {
              @Override protected Integer initialValue() {
                  return nextId.getAndIncrement();
          }
      };
 
      // Returns the current thread's unique ID, assigning it if necessary
      public static int get() {
          return threadId.get();
      }
  }

/* 
    利用官方源码。
    官方解释当每个线程使用这个ThreadLocal类变量值时,都会在线程中创建一个副本
    所以,当我在下面创建两个线程的时候,A线程中有一个threadId副本。
    B线程中也有一个threadId副本。如果在假定A线程比B线程先执行。
    那么A中threadId为0,B中threadId为1。且A中能访问B中的副本,因为官方都说了
    每个线程都有自己的、独立初始化的变量副本。(在后续中慢慢解释)
*/
public class ThreadLocalTest {
    public static void main(String[] args) {
        new Thread(ThreadId::get).start();// A线程
        new Thread(ThreadId::get).start();// B线程
    }
}

整体概述

先看看ThreadLocal整体的结构(从源码中剔除了不需要的),SuppliedThreadLocal内部类我不粘贴,后续会提到。

ThreadLocal中有一个静态内部类ThreadLocalMap,在ThreadLocalMap中维护了一个Entry静态类,还有一个Entry数组名为table的属性。在Entry类中,继承了 WeakReference<ThreadLocal<?>,所以Enrty可以存放ThreadLocal的弱引用。table数组里的每个下标都是根据ThreadLocal中的threadLocalHashCode算出来的,每个线程的threadLocalHashCode都不一样,所以table[i]里存放的该线程里某个ThreadLocal的Entry。

ThreadLocal中的方法都是用来操作ThreadLocalMap对象的。

ThreadLocalMap是用来存储ThreadLocal的弱引用和对应的值

Thread是具体使用的,Thread通过ThreadLocal中的方法操作ThreadLocalMap


// ThreadLocal也是源码,我去掉了大部分的源码,只露了结构
public class ThreadLocal<T> {
    private final int threadLocalHashCode = nextHashCode();
    static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        private Entry[] table;


        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属性:

private final int threadLocalHashCode = nextHashCode();

1、线程标识,不同的线程该值不同;2、在ThreadLocalMap中确定线程的位置;值不同,在ThreadLocalMap中位置不同,线程就能对自己的副本安全访问,避免了与其他线程的冲突。

private static AtomicInteger nextHashCode = new AtomicInteger();

修饰符时static,是类变量。这个是用来返回线程的threadLocalHashCode值的。

private static final int HASH_INCREMENT = 0x61c88647;

修饰符时static,是类变量。这个是has增加量。解释一下为什么是这个值,这个值是斐波那契数列,它在二进制表示下可以产生较好的散列分布特性。主要还是避免和其他线程拥有同样的threadLocalHashCode值

ThreadLocal方法

private static int nextHashCode()

/*
    计算下一个线程的threadLocalHashCode 
    这是属性的定义private final int threadLocalHashCode = nextHashCode();
    调用的就是该方法
*/
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

protected T initialValue()

/*
    在最开始没有set方法,直接调用get方法时,最后就会调用该方法;
    get方法返回的就是该值。
*/
protected T initialValue() {
    return null;
}

public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)

java8出现的该方法,为啥?因为Supplier接口函数也是java8出现的。

创建SuppliedThreadLocal对象时,调用Supplier对象的get方法来获取初始值,并将其设置为线程局部变量的值。每个ThreadLocal都只有一个SuppliedThreadLocal对象。

    /**
     * Creates a thread local variable. The initial value of the variable is
     * determined by invoking the {@code get} method on the {@code Supplier}.
     *
     * @param <S> the type of the thread local's value
     * @param supplier the supplier to be used to determine the initial value
     * @return a new thread local variable
     * @throws NullPointerException if the specified supplier is null
     * @since 1.8
     */
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

public ThreadLocal()

public ThreadLocal() {
}

public T get()

/*
    1、获取ThreadLocalMap存放的值
    ThreadLocalMap.Entry中存放的key是当前的ThreadLocal副本实例,value是对应值
    2、获取SuppliedThreadLocal值(该值调用setInitialValue方法获取)
*/
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();
    }

private T setInitialValue()

/*
    调用get方法时,如果从ThreadLocalMap中获取不到值,则调用该方法。
    该方法中的initialValue方法,SuppliedThreadLocal类重写了,所以
    1.如果该类是SuppliedThreadLocal则调用SuppliedThreadLocal中的对应方法,
    返回之前提到的的withInitial方法中的值。
    2.如果该类是ThreadLocal,ThreadLocal的initialValue返回null。

    后面的Thread.currentThread();,就是给当前线程的ThreadLocalMap设置值。
*/
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;
    }

public void set(T value)

/*
    调用ThreadLocal中的set为当前线程的ThreadLocalMap设置值。
    getMap()方法获取该线程的ThreadLocalMap
    map.set方法是当ThreadLocalMap不为null时,
    表明已经创建了线程已经有ThreadLocalMap,
    所以直接调用ThreadLocalMap进行设置值。set方法后续详细说
    如果不存在ThreadLocalMap ,则需要调用createMap创建一个ThreadLocalMap
*/
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

public void remove()

/*
    Thread.remove是用来在ThreadLocalMap中移除线程的当前ThreadLocal。
    
*/

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

ThreadLocalMap getMap(Thread t)

/*
    获取线程的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

void createMap(Thread t, T firstValue)

/*
    创建ThreadLocalMap并当前ThreadLocal弱引用做key放入到ThreadLocalMap
*/
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap)

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

内部类SuppliedThreadLocal

特殊的一个获取值方法,该类是静态修饰的,且继承了ThreadLocal类,重写了initialValue方法。

调用了构造方法参数(Supplier参数)。在ThreadLocal中通过public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)方法构造一个SuppliedThreadLocal类。并在get方法中,通过initialValue调用该方法。

static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

        private final Supplier<? extends T> supplier;

        SuppliedThreadLocal(Supplier<? extends T> supplier) {
            this.supplier = Objects.requireNonNull(supplier);
        }

        @Override
        protected T initialValue() {
            return supplier.get();
        }
    }

内部类ThreadLocalMap

该类的所有属性和方法都是私有的,Thread线程只能通过ThreadLocal方法间接调用里面的方法。

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private
  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值