JDK1.8源码阅读(十六)java.lang.ThreadLocal

ThreadLocal

定义

/**
*这个类提供线程局部变量。这些变量与
*它们在每个线程中访问一个线程(通过其
*get或set)有自己的、独立初始化的
*变量的副本。threadlocal实例通常是私有的
*类中希望将状态与线程关联的静态字段(例如,
*用户ID或事务ID)。
*
*例如,下面的类生成每个
*线程。
*线程的id是在它第一次调用threadid.get()时分配的。
*在随后的通话中保持不变。
*import java.util.concurrent.atomic.atomicInteger;
*
*public class threadid{

*//包含要分配的下一个线程ID的原子整数

*private static final atomicinteger nextid=新的atomicinteger(0);

*

*//包含每个线程ID的线程局部变量
private static final ThreadLocal;Integer threadId =
*         new ThreadLocal Integer;() {
*             Override protected Integer initialValue() {
*                 return nextId.getAndIncrement();
*         }
*     };
*
*//返回当前线程的唯一id,必要时进行赋值
*     public static int get() {
*         return threadId.get();
*     }
*<p>每个线程都包含对其本地线程副本的隐式引用
*变量,只要线程是活动的并且threadlocal
*实例是可访问的;线程消失后,它的所有副本
*线程本地实例将被垃圾回收(除非其他
*存在对这些副本的引用)。
*
*/
public class ThreadLocal<T> {}

属性

/**
*线程局部变量依赖于附加的每线程线性探测哈希映射
*到每个线程(thread.threadlocals和可继承线程局部变量)。
*threadlocal对象充当键,通过threadlocalhashcode搜索。这是自定义哈希代码
*(仅在threadlocalmaps中有用)消除冲突在连续构造线程局部变量的常见情况下
*由相同的线程使用,而在不太常见的情况。
*/
private final int threadLocalHashCode = nextHashCode();


//下一个要发出的哈希代码。原子更新。开始于零。
private static AtomicInteger nextHashCode = new AtomicInteger();


/**
*连续生成的哈希码之间的差异
*隐式序列线程局部id的近似最优扩散
*两个大小表的幂的乘法散列值。
*/
private static final int HASH_INCREMENT = 0x61c88647;


//内部类
/**
*threadlocal的一个扩展,它从
*指定的supplier
*/
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是一个定制的散列映射,仅适用于
*维护线程本地值。不导出任何操作
*在threadlocal类之外。类是包私有的
*允许在类线程中声明字段。帮助处理
*非常大且使用寿命长的用法,哈希表条目使用
*为钥匙举行会议。但是,由于引用队列不是
*使用时,只有当
*桌子开始没地方了。
*/
static class ThreadLocalMap {

        /**
        *此哈希映射中的条目使用
        *它的主ref字段作为键(它总是
        *线程本地对象)。注意空键(即entry.get()
        *==null)表示不再引用密钥,因此
        *可以从表中删除条目。这些条目被提及
        *在下面的代码中作为“陈旧条目”。
        */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

 

 

方法

//构造方法,创建线程局部变量。 
public ThreadLocal() {}

//返回下一个哈希代码。
private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
}

/**
*返回当前线程的“初始值”
*线程局部变量。此方法将首先调用
*线程使用get访问变量的时间
*方法,除非线程以前调用set
*方法,在这种情况下initialvalue方法将不会
*为线程调用。通常,此方法在
*每个线程最多一次,但在
*随后调用remove,然后调用get。
*这个实现只返回null;如果
*程序员希望线程局部变量具有初始值
* null,threadlocal以外的值必须是
*子类,此方法被重写。通常,一个
*将使用匿名内部类。
protected T initialValue() {
        return null;
}

/**
*创建线程局部变量。变量的初始值是
*通过调用supplier上的 get方法确定。
*
*@param<s>线程本地值的类型
*@param supplier用于确定初始值的供应商
*@返回新的线程局部变量
*@如果指定的供应商为空,则引发nullpointerexception
*@自1.8以来
*/
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
        return new SuppliedThreadLocal<>(supplier);
    }

/**
*返回当前线程副本中的值
*线程局部变量。如果当前线程变量没有
*,它首先初始化为返回的值
*通过调用initialvalue方法。
*
*@返回此线程本地的当前线程值
*/
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();
}


/**
*初始化变量方法,
*以防用户重写set()方法。
*
*@返回初始值
*/
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;
    }


/**
*设置此线程局部变量的当前线程副本
*到指定的值。大多数子类都不需要
*重写此方法,仅依赖initialvalue
*方法设置线程局部变量的值。
*
*@param value要存储在当前线程副本中的值
*这个线程是本地的。
*/
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

/**
*删除此线程本地的当前线程值
*变量。如果此线程局部变量随后
*由当前线程读取,其值将为
*通过调用其 initialvalue方法重新初始化,
*除非它的值是当前线程设置的
*在此期间。这可能导致多次调用
*当前线程中的initialvalue方法。
*
*@自1.5以来

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

/**

*获取与threadlocal关联的Map。在可继承线程本地中重写
*
*@Param 当前线程
*返回map
*/
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

/**
*创建与threadlocal关联的map。
*重写可继承线程本地。
*
*@param 当前线程
*@param firstvalue映射初始项的值
*/
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

/**
*创建继承的线程局部变量map的工厂方法。
*设计为仅从线程构造函数调用。
*
*@param parent map与父线程关联的映射
*@return 包含父级可继承绑定的映射
*/
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

/**
*childValue方法在子类中定义
*InheritableThreadLocal,但在此为
*为了提供createInheritedMap工厂方法
*需要在InheritableThreadLocal中对映射类进行子类化。
*这种技术比嵌入的方法更可取
*方法中测试的实例。
*/
T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

ThreadLocal 的数据结构:实线表示强引用,虚线表示弱引用
每个 Thread 维护一个ThreadLocalMap 映射 table,映射 table 的 key 是 ThreadLocal 实例,value 就是线程存独立的变量副本的地方。
为什么这么设计,而不是由 ThreadLocal 来维护一个以 Thread 为 key 的映射呢?原因如下:
减小 Entry 数组大小:ThreadLocal 数量多,还是 Thread 的数量多,显而易见,使用 ThreadLocal 来当 key 可以减少 Entry 数量
减小内存占用:当 Thread 消亡,对 Thread 实例不在引用,则 GC 后就会清除相关数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值