ThreadLocal

ThreadLocal介绍

ThreadLocal——线程本地变量副本
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

ThreadLocal常用方法

ThreadLocal类接口很简单,常用方法如下:

  • public void set(T value)设置当前线程的线程局部变量的值;
  • public T get()返回当前线程所对应的线程局部变量;
  • public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,此方法为JDK5.0新增的方法。当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度;
  • public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)是JDK1.8新增的方法。创建一个线程本地变量副本。默认值由Supplier的get()方法控制;
  • protected T initialValue()方法的访问修饰符是protected,该方法为第一次调用get方法提供一个初始值。默认情况下,第一次调用get方法返回值null。在使用时,我们一般会重写ThreadLocal的initialValue方法,使第一次调用get方法时返回一个我们设定的初始值。

ThreadLocal的使用

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;


public class ThreadLocalDemo {
    private ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->8);


    private Semaphore semaphore = new Semaphore(1);


    private Random random = new Random();


    public class Worker implements Runnable{


        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int value = threadLocal.get();
            System.out.println(Thread.currentThread().getName() + "threadLocal old value:" + value);
            value = random.nextInt();
            threadLocal.set(value);
            System.out.println(Thread.currentThread().getName() + "theadLocal new value:" + value);
            System.out.println(Thread.currentThread().getName() + "threadLocal last value:" + value);
            semaphore.release();
            threadLocal.remove();
        }
    }


    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(3);
        ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
        es.execute(threadLocalDemo.new Worker());
        es.execute(threadLocalDemo.new Worker());
        es.execute(threadLocalDemo.new Worker());
        es.shutdown();
    }
}

ThreadLocal源码分析

在这里插入图片描述

/** ===========TheadLocal中的get()源码分析
=========== */
/**
* TheadLocal中的get()源码分析
*/
public T get() {
// 获取当前的线程
Thread t = Thread.currentThread();
// 调⽤getMap()⽅法获取线程t内的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap对象⾮空,则进⾏写⼊操作
if (map != null) {
// ThreadLocalMap的结构为K-V的Map结构
// ThreadLocalMap的内部类Entry(与HashMap类似)
// key是ThreadLocal对象,Value是ThreadLocal对象的值
ThreadLocalMap.Entry e = map.getEntry(this);
// Entry⾮空即当前这个ThreadLocal对象的K-V存在
if (e != null) {
// ThreadLocalMap.Entry中的value值
T result = (T)e.value;
// 返回value
return result;
}
}
// 如果ThreadLocalMap对象为空,则进⾏初始化
return setInitialValue();
}
/**
* 返回与当前的线程对象相关的ThreadLocalMap对象
*/
ThreadLocalMap getMap(Thread t) {
// 返回线程t的threadLocals对象
return t.threadLocals;
}
/* Thread类的属性 */
ThreadLocal.ThreadLocalMap threadLocals = null;
/**
* 设置初始值的⽅法
*/
private T setInitialValue() {
// 获取初始值
T value = initialValue();
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap对象⾮空
if (map != null) // 往Map中设置K-Vmap.set(this, value);
else
// 如果ThreadLocalMap对象为空
// 创建ThreadLocalMap对象
createMap(t, value);
// 返回initialValue()⽅法的值
return value;
}
/**
* 返回ThreadLocal的默认值
*/
protected T initialValue() {
return null;
}
/**
* 创建ThreadLocalMap对象
*/
void createMap(Thread t, T firstValue) {
// 调⽤ThreadLocalMap的构造器并给线程内的ThreadLocalMap赋值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/** ==================TheadLocal中的set()源码分析
================== */
/**
* 设置ThreadLocal变量的值
*/
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果当前线程的ThreadLocalMap⾮空
if (map != null)
// 往ThreadLocalMap中添加K-Vmap.set(this, value);
else
// 如果当前线程的ThreadLocalMap为空
// 创建ThreadLocalMap对象
createMap(t, value);
}
/**
* ThreadLocalMap是
⼀
种特殊的仅仅⽤于存储线程本地变量的HashMap
* ThreadLocalMap的所有操作都封装在ThreadLocal的内部
* ThreadLocalMap的key是弱引⽤类型
*/static class ThreadLocalMap {
/**
* Entry 类继承⾃WeakReference
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** ThreadLocal保存的value */
Object value;
// 构造器,key是ThreadLocal,value是v
Entry(ThreadLocal<?> k, Object v) {
// ⽗类构造器,key是ThreadLocal
super(k);
// 设置value为v
value = v;
}
}
/**
* hash表的容量,必须为2的整数次幂
*/
private static final int INITIAL_CAPACITY = 16;
/**
* hash底层的数组,⻓度必须为2的整数次幂
*/
private Entry[] table;
/**
* hash表中元素的个数即Entry的数量
*/
private int size = 0;
/**
* hash表扩容的阈值
*/
private int threshold; // Default to 0
/**
* 设置hash表扩容的阈值。负载因⼦ * 2 / 3
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* 增加i并对len取模,并控制不超过len
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/** * 减少i并对len取模,并控制不⼩于0
*/
private static int prevIndex(int i, int len) {
return ((i-1 >= 0) ? i-1 : len-1);
}
/**
* 初始化ThreadLocalMap,⾄少有
⼀
个Entry才初始化ThreadLocalMap
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)
{
// 初始化hash表,默认的容量16
table = new Entry[INITIAL_CAPACITY];
// key的hashcode & 1111,即保留key的hashcode的低4位
int i = firstKey.threadLocalHashCode &(INITIAL_CAPACITY-1);
// hash表的第i个位置存放⼀个新的 Entry对象
table[i] = new Entry(firstKey, firstValue);
// hash表⼤⼩为1
size = 1;
// 设置扩容的阈值
setThreshold(INITIAL_CAPACITY);
}
/**
* 获取key对应的entry
*/
private Entry getEntry(ThreadLocal<?> key) {
// key的hashcode & 1111,即保留key的hashcode的低4位
int i = key.threadLocalHashCode & (table.length-1);
// 获取hash表位置i处的Entry对象
Entry e = table[i];
// e⾮空且e的key等于key,说明就是要查找的Entry对象
if (e != null && e.
get() == key)
// 返回Entry对象e
return e;
else
// 没有找到对应的Entry,需要继续查找
return getEntryAfterMiss(key, i, e);
}
/**
* 直接在hash表对应的槽位上没有找到对饮的Entry
* 采⽤线性探测法继续在hash表上查找
*/
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i,
Entry e) {
// hash表的Entry数组
Entry[] tab = table; // Entry数组的⻓度
int len = tab.length;
// 如果e⾮空
while (e != null) {
// 获取e的key
ThreadLocal<?> k = e.
get();
// 如果k等于key,找到对应的Entry
if (k == key)
// 返回Entry对象
return e;
// 如果k为空
if (k == null)
// 移除这个失效的Entry对象
expungeStaleEntry(i);
else
// 修改i为hash表的下⼀个位置
i = nextIndex(i, len);
// 修改e为tab[i],进⼊下⼀次循环
e = tab[i];
}
// 没有找到对应的Entry对象
return null;
}
/**
* 往Hash表存储数据
*/
private void set(ThreadLocal<?> key, Object value) {
// hash表Entry数组
Entry[] tab = table;
// hash表Entry数组的⻓度
int len = tab.length;
// key的hashcode & 1111,即保留key的hashcode的低4位
int i = key.threadLocalHashCode & (len-1);
// 如果hash表Entry数组第i个位置的Entry⾮空,继续向后探索
// 因为是通过线性探索法处理hash碰撞的
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
// 获取e的key
ThreadLocal<?> k = e.
get();
// 如果k等于key,找到了对应的Entry
// 更新Entry的值
e.value = value;
// 跳出循环
return;
}
// 如果Entry的key为空
if (k == null) { // 需要替换旧的Entry
replaceStaleEntry(key, value, i);
return;
}
}
// 如果hash表i位置没有Entry,直接创建新的Entry并存⼊位置i
tab[i] = new Entry(key, value);
// hash表⻓度加1
int sz = ++size;
// 如果没有可以清除的Entry并且hash表的⻓度⼤于等于扩容阈值
if (!cleanSomeSlots(i, sz) && sz >= threshold)
// 重新调整hash表的⼤⼩
rehash();
}
/**
* 删除key对应的entry,与set()操作相反
*/
private void remove(ThreadLocal<?> key) {
// hash表Entry数组
Entry[] tab = table;
// hash表Entry数组的⻓度
int len = tab.length;
// key的hashcode & 1111,即保留key的hashcode的低4位
int i = key.threadLocalHashCode & (len-1);
// 如果hash表Entry数组第i个位置的Entry⾮空,继续向后探索
// 因为是通过线性探索法处理hash碰撞的
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.
get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
/**
* 替换失效的Entry
* 副作⽤是清除两个空的位置间的就的Entry对象
*/
private void replaceStaleEntry(ThreadLocal<?> key, Object
value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
// 备份以检查当前运⾏中的先前失效的Entry
// 待清除的失效的Entry得位置 int slotToExpunge = staleSlot;
// 从staleSlot位置向前搜索,找到hash表第⼀个Entry为空的位置
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.
get() == null)
slotToExpunge = i;
// 从staleSlot位置向后搜索
// 找到hash表第
⼀
个Entry为空的位置,结束循环
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
// 获取e的key
ThreadLocal<?> k = e.
get();
// 找到对应的Entry
if (k == key) {
// 找到对应的Entry
e.value = value;
// 互换tab[i]和entry的位置
// 因为ThreadLocalMap是根据线性探测法来解决冲突的
// 因此可能会出现key的哈希值相同但散落位置不连续的情况
// 为了在⼀定程度上提⾼查找哈希值相同entry节点的效率
// 交换⼀下位置会是更好的选择
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// Start expunge at preceding stale entry if it
exists
if (slotToExpunge == staleSlot)
slotToExpunge = i;
//执⾏清理⼯作
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// If we didn't find stale entry on backward scan,the
// first stale entry seen while scanning for key isthe
// first still present in the run.
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// 如果没有找到key对应的Entry,则创建新的Entry并放⼊staleSlot位置
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// 如果还有其他的失效的Entry将其清除
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge),
len);
}
/**
* 清理失效的Entry
*/
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 清理staleSlot位置的Entry
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// 调整hash表直到后⾯第⼀个tab[i]为null为⽌
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.
get();
// 如果key为null,将该entry置为null
if (k == null) {
e.value = null;
tab[i] = null;
size--;
}
else {
// 下⾯是新计算⼀下hash值,如果位置与当前位置不同
// 需要重新找⼀个位置放该节点。⽤的也是线性探测法
int h = k.threadLocalHashCode & (len-1);
if (h != i) {
tab[i] = null;
// Unlike Knuth 6.4 Algorithm R, we mustscan until
// null because multiple entries could havebeen stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
} return i;
}
/**
* 清理⾮null但是key为null的Entry
* 参数n决定了for循环要执⾏的次数。
* 如果每次循环tab[i]均不需要清理,最多会执⾏logn次。
* 如果有需要清理的节点,就会调⽤expungeStaleEntry()⽅法清理
*/
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.
get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
/**
* 重新调整hash表
*/
private void rehash() {
// 清理所有失效的Entry
expungeStaleEntries();
// 如果size等于等于threshold * 3/4
if (size >= threshold
-
threshold / 4)
// 进⾏扩容
resize();
}
/**
* hash表容量加倍
*/
private void resize() {
// 旧的hash表
Entry[] oldTab = table;
// 旧的hash表的容量
int oldLen = oldTab.length;
// 新的hash表的容量= oldLen * 2
int newLen = oldLen * 2;
// 新的hash表
Entry[] newTab = new Entry[newLen];
int count = 0; //遍历旧的hash表
for (int j = 0; j < oldLen; ++j) {
//遍历旧的hash表的第j个位置的Entry
Entry e = oldTab[j];
//如果Entry⾮空
if (e != null) {
// Entry的key
ThreadLocal<?> k = e.
get();
// Entry的key失效
if (k == null) {
// 把Entry的value置为空
e.value = null; // Help the GC
} else {
// 重新计算新的hash值
int h = k.threadLocalHashCode & (newLen-1);
// 线性探测法计算Entry在新的hash表中的存储位置
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
// 设置新的扩容阈值
setThreshold(newLen);
// 设置hash表的⼤⼩
size = count;
// 修改table引⽤新的hash表
table = newTab;
}
/**
* 清理hash表所有的失效元素
*/
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
// 遍历整个Entry数组全量清理失效的Entry
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.
get() == null)
expungeStaleEntry(j);
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值