目录
2.2.1、Entry弱引用WeakReference,防止内存泄漏
-
一、介绍
ThreadLocal是指线程局部变量,多个线程并发运行的时候,使用ThreadLocal装饰的变量在每个线程里都是单独使用的,Thread线程本身就是一个类,ThreadLocal是Thread里面的一个局部变量。所以ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的和普通的类一样的。由于每个线程拥有了自己的变量,所以消除了多线程情况下的竞争关系。
-
二、原理
ThreadLocal这个类主要做用为key泛型ThreadLocal<T>,都是作为ThreadLocalMap的key值,每个线程都要自己的一个map,map是一个数组的数据结构存储数据,每个元素是一个Entry,entry的key是threadlocal的引用,也就是当前变量的副本,value就是set的值。
2.1、ThreadLocal
ThreadLocal这个类主要做用为key泛型ThreadLocal<T>,都是作为ThreadLocalMap的key值和hashmap实现原理一样主要一个方法threadLocalHashCode = nextHashCode(),通过hashcode计算value位置
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* The difference between successively generated hash codes - turns
* implicit sequential thread-local IDs into near-optimally spread
* multiplicative hash values for power-of-two-sized tables.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
2.1.1、set关键方法
/**
* T value存入ThreadLocalMap真实值
*/
public void set(T value) {
/**
* 关键方法获取当前线程从而做到线程数据隔离
*/
Thread t = Thread.currentThread();
/**
* 通过thread获取当前线程内对象的ThreadLocalMap,这是个数组结构
*/
ThreadLocalMap map = getMap(t);
/**
* 如果第一次使用需要创建ThreadLocalMap
*/
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.1.2、get关键方法
public T get() {
/**
* 关键方法获取当前线程从而做到线程数据隔离
*/
Thread t = Thread.currentThread();
/**
* 通过thread获取当前线程内对象的ThreadLocalMap,这是个数组结构
*/
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() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
2.2、ThreadLocalMap
public class Thread implements Runnable {
.
.
.
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
.
.
.
}
Thread线程类持有ThreadLocal.ThreadLocalMap,ThreadLocalMap类似Map实现方式里面由一个数组结构
static class ThreadLocalMap {
/**
* 默认数组16.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* table数组存储value
*/
private Entry[] table;
/**
* table里面value size
*/
private int size = 0;
/**
* 和map加载因子一样
*/
private int threshold; // Default to 0
/**
* 计算扩容阈值 len 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
2.2.1、Entry弱引用WeakReference,防止内存泄漏
tatic class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
2.2.2、set关键方法
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
//通过位运算计算在table里面的位置和求模结果一样更加高效
int i = key.threadLocalHashCode & (len-1);
//如果已经存在,hash位置碰撞
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//如果是同一个key直接替换返回
if (k == key) {
e.value = value;
return;
}
//重新计算位置放入素组内
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//如果对应位置不存在直接赋值
tab[i] = new Entry(key, value);
int sz = ++size;
//判断是否需要扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
2.2.3、get关键方法
private Entry getEntry(ThreadLocal<?> key) {
//获取index下标通过hashcode
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
//如果取到了值并且key相同直接返回
if (e != null && e.get() == key)
return e;
else
//如果没有取到或有碰撞需要再判断
return getEntryAfterMiss(key, i, e);
}
-
三、使用
import java.util.Random;
public class ThreadLocalTest {
private final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
//ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
@Override
protected Object initialValue() {
System.out.println("调用get()时,当前线程共享变量没有设置,调initialValue获取默认值");
return null;
}
};
public void testThreadLocal() {
new Thread(new MyRunnable("String测试1111",threadLocal),"thread1").start();
new Thread(new MyRunnable(12345,threadLocal),"thread2").start();
new Thread(new MyRunnable("String测试2222",threadLocal),"thread3").start();
new Thread(new MyRunnable(66666,threadLocal),"thread4").start();
}
public static class MyRunnable<T> implements Runnable {
private T value;
private ThreadLocal threadLocal;
public MyRunnable(T o,ThreadLocal threadLocal){
value = o;
this.threadLocal = threadLocal;
}
@Override
public void run() {
//不同线程中设置值
threadLocal.set(value);
try {
Random random = new Random();
Thread.sleep(random.nextInt(200) + 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + " threadLocal "+threadLocal.get());
}
}
}
-
四、总结
ThreadLocal 适用于如下两种场景
- 每个线程需要有自己单独的实例
- 实例需要在多个方法中共享,但不希望被多线程共享
对于第一点,每个线程拥有自己实例,实现它的方式很多。例如可以在线程内部构建一个单独的实例。ThreadLoca 可以以非常方便的形式满足该需求。
对于第二点,可以在满足第一点(每个线程有自己的实例)的条件下,通过方法间引用传递的形式实现。ThreadLocal 使得代码耦合度更低,且实现更优雅。