ThreadLocal、InheritableThreadLocal、ThreadLocalRandom

8 篇文章 0 订阅

ThreadLocal

ThreadLocalJDk包提供的,它提供了线程的本地变量,也就是如果你创建了一个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重写了三个方法childValuegetMapcreateMap,使用了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);
    }
}

childValueThread的构建方法调用时执行。

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变量里面。

当线程调用ThreadLocalRandomcurrent方法时,会初始化此种子变量。

主要代码实现逻辑

// 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变量中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值