ThreadLocal
ThreadLocal
是JDk
包提供的,它提供了线程的本地变量,也就是如果你创建了一个ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。
当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。
使用示例
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
private static void print(String str) {
System.out.println(str + ":" + threadLocal.get());
threadLocal.remove();
}
public static void main(String[] args) {
new Thread(() -> {
threadLocal.set("test t1 ThreadLocal variable");
print("t1");
System.out.println("t1 remove after:" + threadLocal.get());
}).start();
new Thread(() -> {
threadLocal.set("test t2 ThreadLocal variable");
print("t2");
System.out.println("t2 remove after:" + threadLocal.get());
}).start();
}
}
/*
t1:test t1 ThreadLocal variable
t2:test t2 ThreadLocal variable
t1 remove after:null
t2 remove after:null
*/
源码
Thread 类中有两个变量
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
每个线程的本地变量不是存放在ThreadLocal
实例里面,而是存放在在调用线程的threadLocals
变量里面。
ThreadLocal
就是一个工具壳,通过set
方法把value
值放入线程的threadLocals
变量里面并存放起来,当调用线程的get
方法时,再从当前线程的threadLocals
变量里面取出。
如果线程一直不终止,那么这个本地变量会一直存放在调用线程的threadLocals
变量里面,所以不需要本地变量的时可以通过remove
方法将其删除。
set
public void set(T value) {
Thread t = Thread.currentThread();
// 获取当前线程的 threadLocals 变量
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// 第一次调用时就创建 ThreadLocalMap
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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;
}
}
// threadLocals 变量为空,则初始化当前线程的 threadLocals 变量
return setInitialValue();
}
private T setInitialValue() {
// 初始化 value 为 null
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
remove
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
InheritableThreadLocal
同一个ThreadLocal
变量在父线程中被设置后,在子线程中是获取不到的。
InheritableThreadLocal
可以解决这个问题。
使用示例
public class InheritableThreadLocalTest {
private static ThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
inheritableThreadLocal.set("hello world");
new Thread(() -> {
// 输入 inheritableThreadLocal 中的值
System.out.println("thread: " + inheritableThreadLocal.get());
}).start();
System.out.println("main: " + inheritableThreadLocal.get());
// 习惯性回收不用的变量
inheritableThreadLocal.remove();
}
}
/*
main: hello world
thread: hello world
*/
源码
InheritableThreadLocal
重写了三个方法childValue
、getMap
、createMap
,使用了Thread
的变量inheritableThreadLocals
.
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
childValue
在Thread
的构建方法调用时执行。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
init(g, target, name, stackSize, null);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
// 获取父线程
Thread parent = currentThread();
// ......
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
tid = nextThreadID();
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
// 获取父线程 ThreadLocalMap 的所有 Entry
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
// Set the resize threshold to maintain at worst a 2/3 load factor.
setThreshold(len);
// 初始化当前线程的 table 值
table = new Entry[len];
// 遍历父线程的 table
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 调用 InheritableThreadLocal 重写的方法 childValue
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
// 将新生成的 Entry 方到当前线程的 table 中
table[h] = c;
size++;
}
}
}
}
Random
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
// 1.根据老的种子生成新的种子
int r = next(31);
// 2.根据新的种子计算随机数
int m = bound - 1;
if ((bound & m) == 0) // i.e., bound is a power of 2
r = (int)((bound * (long)r) >> 31);
else {
for (int u = r; u - (r = u % bound) + m < 0; u = next(31))
;
}
return r;
}
// 采用CAS的方式生成新的种子,多线程下进行CAS只会有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
ThreadLocalRandom
为了弥补Random的缺陷。新增了ThreadLocalRandom
,在JUC
包下。
ThreadLocalRandom
继承了Random
,并重写了nextInt
等方法,没有使用父类的原子性种子变量。
ThreadLocalRandom
中并没有存放具体的种子,而是存放在Thread
中的threadLocalRandomSeed
变量里面。
当线程调用ThreadLocalRandom
的current
方法时,会初始化此种子变量。
主要代码实现逻辑
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
SEED = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSeed"));
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomSecondarySeed"));
} catch (Exception e) {
throw new Error(e);
}
}
current
public static ThreadLocalRandom current() {
// 判断 threadLocalRandomProbe 是否为0
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
// 计算当前线程的初始化种子变量
localInit();
return instance;
}
static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
nextInt
public int nextInt(int bound) {
if (bound <= 0)
throw new IllegalArgumentException(BadBound);
// 根据当前线程种的种子计算新种子
int r = mix32(nextSeed());
// 根据新种子和bound计算随机数
int m = bound - 1;
if ((bound & m) == 0) // power of two
r &= m;
else { // reject over-represented candidates
for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1)
;
}
return r;
}
nextSeed
final long nextSeed() {
Thread t; long r; // read and update per-thread seed
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
首先使用r = UNSAFE.getLong(t, SEED)
获取当前线程中threadLocalRandomSeed
变量的值,然后在种子的基础上加GAMMA
值作为新种子。
然后使用UNSAFE.putLong
把新种子放入当前线程的threadLocalRandomSeed
变量中。