ThreadLocal类、ThreadLocalRandom 类原理剖析

一、ThreadLocal 类

    多线程访问同一个共享变量时,特别容易出现 并发问题,特别是 在多个线程 需要对 一个共享变量 进行 写入 时 。为了保证线程安全,一般会在访问共享变量时,进行适当的同步。
    同步的措施一般是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担。而 ThreadLocal 的作用:当创建一个变量后,每个线程对其进行访问的时候,访问的是自己线程的变量
    ThreadLocal 提供了线程本地变量,也就是说,如果创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程 都会有这个变量的一个 本地副本。 当多个线程操作这个变量时,实际操作的时 自己本地内存 里面的变量,从而避免了线程安全的问题。创建一个 ThreadLocal 变量后,每个线程都会复制一个变量到自己的本地内存,如图所示:
在这里插入图片描述

例 👀

public class ThreadLocalTest {

    // 创建 THreadLocal 变量
    static ThreadLocal<String> localVariable = new ThreadLocal<>();

    static void print(String str) {

        // 打印 当前线程 本地内存中 localVariable 变量的值
        System.out.println(str + ":" + localVariable.get());

        // 清除 当前线程 本地内存中 localVariable 变量
        localVariable.remove();
    }

    public static void main(String[] args) {
        //创建线程 One
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                // 设置 线程One 中本地变量 localVariable 的值
                localVariable.set("threadOne local variable");

                // 调用打印函数
                print("threadOne");

                //打印本地变量值
                System.out.println("threadOne remove after" + ":" + localVariable.get());
            }
        });

        // 创建线程 Two
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                // 设置线程Two 中本地变量 localVariable 的值
                localVariable.set("threadTwo local variable");

                // 调用打印函数
                print("threadTwo");

                // 打印本地变量值
                System.out.println("threadTwo remove after" + ":" + localVariable.get());
            }
        });

        // 启动线程
        threadOne.start();
        threadTwo.start();
    }
}

运行结果:
在这里插入图片描述
     线程的 run 方法中 ,通过 set 方法 设置了 localVariable 的值,这其实设置的是 线程 本地内存中 的一个副本,这个副本是其他线程访问不了的。
    

1、ThreadLocal 相关类的类图结构

在这里插入图片描述

     Thread 类中有一个 threadLocals 和 一个 inheritableThreadLocals,它们都是 ThreadLocalMap 类型的变量,默认情况下,每个线程中这两个变量都为 null ,只有当线程 第一次调用 ThreadLocal 的 set 或 get 方法时,才会创建它们:

/* 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;

(pertaining to:有关;maintain:维护)
    

2、ThradLocalMap
 static class ThreadLocalMap {

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

     可以看到, ThreadLocalMap 是 ThreadLocal 类 的 静态内部类,是一个定制化的 HashMap。(❓ 为什么是 map 结构呢❓ 因为这样的话 每个线程可以关联 多个 ThreadLocal 变量。该 map 默认的大小是16,即能存储16个键值对,超过后会扩容。)Entry 的 key 就是 ThreadLocal,而 value 就是值。同时,Entry也继承 WeakReference,所以说 Entry 所对应 key(ThreadLocal实例)的引用为一个弱引用

     每个线程的本地变量 不是存放在 ThreadLocal 实例里面的,而是存放在 调用线程的 threadLocals 变量中。 也就是说,ThreadLocal 类型的本地变量 存放在具体的线程内存空间中,ThreadLocal 就是一个工具壳,它通过 set 方法把 value 值放入调用线程的 threadLocals 中 并存放起来,当调用线程调用它的 get 方法时,再从当前线程的 threadLocals 变量中 将其拿出来使用。如果调用线程一直不终止 ,那么这个本地变量 会一直 存放在 调用线程 的 threadlocals 变量里面,所以 当不需要使用本地变量 时 ,可以通过调用 ThreadLocal 变量的 remove 方法,从当前线程的 threadLocals 中删除 该本地变量。
    

2、ThreadLocal 的 set 方法
    public void set(T value) {
        Thread t = Thread.currentThread();
        //(1)
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            //(2)
            createMap(t, value);
    }

(1):

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    getMap(t) 的作用是 获取线程自己的变量 threadLocals ,threadLocals 被绑定到了线程的成员变量上。

    可以看到, set 方法 先获取当前线程 t ,然后把当前线程作为 key 去寻找对应的线程变量,找到 则设置,key 是 当前 ThreadLocal 实例对象的引用,value 就是 通过 set 方法想要设置的值,说明是第一次调用,就创建 当前线程对应的 HashMap:
(2):

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    

3、ThreadLocal 的 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;
            }
        }
        return setInitialValue();
    }

    可以看到 ,get 方法也是先获取当前线程 t,调用 getMap 方法,获取当前线程的 threadLocals 变量,如果当前线程的 threadLocals 变量不为 null,则直接返回 当前线程绑定的本地变量,(也就是 threadLocals 具体的那个 entry )否则【当前线程的 threadLocals 变量为 null】,就执行 setInitialValue() 进行初始化:

 private T setInitialValue() {
       //(1)
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        //(2)
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

    这个初始化方法中,获取一遍当前线程,然后获取了当前线程的 threadLocals 变量 。如果 这个变量不为 null,就把 threadLocals 的 key 设置为 ThreadLocal 实例对象的引用,value 设置成 null (❓为什么需要 这两个 ”又“ 呢❓ setInitialValue()方法只在 get() 末尾被调用。 这块儿的逻辑是这样的,如果没有先 set 再 get,那么 map 为 null,调用初始化方法 setInitialValue(),获取当前线程,并创建map createMap(t, value);;【经过 debug 验证】如果先 set 过值了,但是又 remove 了,那么在 get() 方法中,map 不为 null ,但是 e 为 null,这种情况,也是要走 return setInitialValue(); 的,获取当前线程,并把 value 值设置为 null。)
(1):

  protected T initialValue() {
        return null;
    }
4、ThreadLocal 的 remove 方法
    public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
 private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

🎭总结:
    在每个线程内部都有一个 名为 threadLocals 的成员变量,该变量的类型 是 ThreadLocalMap,其中 key 是我们定义的 ThreadLocals 变量的 this 引用,value 为我们使用 set 方法设置的值。每个线程的本地变量存放在 线程自己的内存变量 threadLocals 中,如果当前线程一直不消亡,那么这些本地变量 会一直存在,所以可能会造成内存溢出,因此 使用完毕后 要记得调用 ThreadLocal 的 remove 方法 删除对应线程的 threadLocals 的本地变量。


内存溢出 与 内存泄漏
  • 内存溢出
        程序运行过程中,无法申请到足够的内存,而导致的一种错误。通常发生于 OLD 段 或 Perm段 垃圾回收后 仍然无内存容纳新的 Java 对象的情况。
  • 内存泄漏
        存在一些被分配的对象,这些对象有下面两个特点:
    (1)这些对象是可达的,即在有向图中,存在通路可以与其相连;
    (2)这些对象是无用的,即程序以后不会再使用这些对象。
        如果对象满足这两个条件,这些对象就可以判定为 Java 中的内存泄漏,这些对象不会被 GC 所回收,然而它却占用内存

5、ThreadLocal 不支持继承性

例👀:

package ThreadLocalPack;

public class ThreadLocalTest2 {

    //创建线程变量
    public static ThreadLocal<String> threadLocal=new ThreadLocal<>();

    public static void main(String[] args) {
        //设置线程变量
        threadLocal.set("hello");

        //启动子线程
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                //子线程输出线程变量的值
                System.out.println("thread:"+threadLocal.get());
            }
        });

        thread.start();

        //主线程输出线程变量的值
        System.out.println("main:"+threadLocal.get());

    }
}

运行结果:
在这里插入图片描述
    可以看到,同一个 ThreadLocal 变量,在父线程(主线程)中被设置值后,在 子线程 中 是获取不到的,因为 在子线程 thread 中 调用 get() 方法时,当前线程是 thread ,而这里 调用 set 方法设置 线程变量的是 main 线程,自然 子线程访问时 返回的就是 null 了,而如果想让 子线程 能够访问到 父线程中的值,就需要:
    

6、InheritableThreadLocal 类

    InheritableThreadLocal 类 继承自 ThreadLocal,并重写了三个方法。可以让子线程访问 在 父线程中设置的 值。它的源码:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    //(1)
    protected T childValue(T parentValue) {
        return parentValue;
    }

   //(2)
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    //(3)
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

    从 (3) void createMap(Thread t, T firstValue) 方法可以看出,创建的是 当前线程的 inheritableThreadLocals 变量的实例,而不再是 threadLocals 。由 (2)ThreadLocalMap getMap(Thread t) 方法可以看出,当调用 get 获取当前线程内部的 map 变量时,获取的是 inheritableThreadLocals 而不再是 threadLocals。 也就是说,在 InheritableThreadLocal 类 中,变量 inheritableThreadLocals 代替了 threadLocals。
    对 (1)childValue(T parentValue) 方法 find usages 查询无果:
在这里插入图片描述
    在 Thread 类的源码 中 可以看到:

 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

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, true);
    }
 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ... ...

        // 获取当前线程
        Thread parent = currentThread();
        
        ... ...
 
        // 如果父线程的 inheritableThreadLocals 不为 null
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
           //(1) 设置子线程中的 inheritableThreadLocals 
            this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
            
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

(1):(👉 注意:传入的参数是 parent.inheritableThreadLocals)

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

    可以看到,在 createInheritedMap 内部,使用父线程的 inheritableThreadLocals 变量作为构造函数,创建了一个新的 ThreadLocalMap ,然后在 init 方法中,赋值给了 子线程的 inheritableThreadLocals 变量。再来看看 ThreadLocalMap 的构造方法:

 private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            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) {
                        //(1)
                        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);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

    可以看到,在该构造方法中,把父线程的 inheritableThreadLocals 变量的值 复制到 新的 ThreadLocalMap 中,(1)处调用的 childValue 就是 InheritableThreadLocal 类 重写的代码。
    🎭总结:
    InheritableThreadLocal 类 通过重写 getMap 和 createMap 方法 ,让本地变量 保存到了具体线程的 inheritableThreadLocals 变量里面,那么线程在通过 InheritableThreadLocal 类实例的 set 或者 get 方法设置变量时,就会创建当前线程的 inheritableThreadLocals 变量。当父线程创建子线程时,Thread 的构造函数会把父线程中 inheritableThreadLocals 变量中的本地变量 复制一份保存到 子线程的 inheritableThreadLocals 中。
    所以把例子中的代码修改为:

 public static ThreadLocal<String> threadLocal=new InheritableThreadLocal<>();

运行结果:
在这里插入图片描述
    在什么情况下 需要子线程可以获取父线程的 threadlocal 变量呢 ?比如 子线程需要使用 存放在 threadlocal 变量中的用户登录信息;再比如 一些中间件需要把统一的 id 追的整个调用链路记录下来。
    这篇博文关于 ThreadLocal 写得挺好,可以作为补充:
https://blog.csdn.net/xiaoxiaole0313/article/details/106132269


    JUC 包中的 ThreadLocalRandom ,是 JDK 7 新增的,它弥补了 Random 类在多线程下的缺陷,它是借鉴 ThreadLocal 的思想实现的。


    

二、ThreadLocalRandom

1、Random 类及其局限性

    在 JDK 7 之前,java.util.Random 是使用比较广泛的 随机数生成工具类,而且 java.lang.Math 中的随机数生成 也是使用 java.util.Random 实例。

例👀:

import java.util.Random;

public class RandomTest {
    public static void main(String[] args) {

        // 创建一个默认种子的 随机数生成器
        Random random=new Random();
        
        // 输出 10 个在 0~5 (包含0,不包含5)之间的随机数
        for(int i=0;i<10;i++){
            System.out.println(random.nextInt(5));
        }
    }
}

运行结果:
在这里插入图片描述
    随机数的生成需要一个默认的种子🌱 ,这个种子是一个 long 类型的数字,可以由创建 Random 对象时,通过构造方法指定,如果不指定就会在默认构造方法的内部生成一个默认值。

  • nextInt(int bound) 源码:
  public int nextInt(int bound) {
  		// 参数检查
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
            
		// 根据老种子生成新种子
		//(1)
        int r = next(31);

		// 根据新种子计算随机数
        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;
    }

    可以看出,新的 随机数的生成需要两个步骤:
(1) 首先根据老的种子生成 新的种子
(2)其次根据新的种子来计算新的 随机数
    在单线程下 ,每次调用 nextInt 都是根据 老的种子 计算处新的种子,可以保证随机数产生的随机性;
    而 对于多线程,当多个线程根据同一个老种子计算新种子时,第一个线程的新种子 被计算出来后,第二个线程要丢弃自己老的种子,而使用 第一个线程的新种子 来计算自己的新种子,依此类推,只有保证了这个,才能保证在多线程下产生的随机数是随机 的, Random 类中使用了 一个原子变量 AtomicLong seed 达到了这样的效果,在创建 Random 对象时 初始化的种子就被保存到了 种子变量里。
(1):

protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
        	// 获取当前原子变量 种子的值
            oldseed = seed.get();
	
			// 根据当前种子值 计算新的种子
            nextseed = (oldseed * multiplier + addend) & mask;

		// CAS 操作,保证只有一个线程可以更新老的种子为新的,
		//失败的种子会通过循环 重新获取更新后的种子作为当前种子 去计算
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

🎭总结:
    每个 Random 实例中 都有一个 原子性的种子变量 用来记录当前的种子值。当要生成新的随机数时,需要根据当前种子 计算新的种子 并 更新回种子变量。在多线程下 使用 单个 Random 实例生成随机数时,当多个线程同时计算随机数 来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新 是 CAS 操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能。
在这里插入图片描述


    而如果每个线程都维护一个变量,则每个线程生成随机数时,都根据自己老的种子计算新的种子,并使用新种子更新老的种子,再根据新种子计算随机数。 就不会存在竞争问题啦,这会大大提高并发性能。

2、ThreadLocalRandom 类的使用

在这里插入图片描述
例 👀 :

public static void main(String[] args) {

		// 获取一个随机数生成器
        ThreadLocalRandom random=ThreadLocalRandom.current();
        for(int i=0;i<10;i++)
        {
        	// 输出 10 个在 0~5 (包含0,不包含5)之间的随机数
            System.out.println(random.nextInt(5));
        }
    }

运行结果:
在这里插入图片描述

3、ThreadLocalRandom 相关类的类图结构

在这里插入图片描述
    可以看到,ThreadLocalRandom 类 继承了 Random 类 ,并重写了 nextInt 方法,在 ThreadLocalRandom 类中并没有使用 继承自 Random 类的原子性种子变量,在 ThreadLocalRandom 类 中 并没有存放具体的种子,具体的种子是存放在 具体的调用线程的 threadLocalRandomSeed 变量里面。ThreadLocalRandom 类 类似于 ThreadLocal 类,就是个 工具类 。当线程调用 ThreadLocalRandom 类 的 current 方法时,ThreadLocalRandom 类 负责初始化 调用线程的 threadLocalRandomSeed 变量,也就是 初始化种子。
    当调用 ThraeadLocalRandom 的 nextInt 方法时,实际上是 获取当前线程的 threadLocalRandomSeed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的 threadLocalRandomSeed 变量,而后 再根据新种子 并使用具体算法 计算随机数。要注意的是 threadLocalRandomSeed 变量 就是 Thread 类里的一个普通 long 变量,并不是原子性的:(因为这个变量是线程级别的,所以不需要使用原子性)

  /** The current seed for a ThreadLocalRandom */
    @sun.misc.Contended("tlr")
    long threadLocalRandomSeed;

    其中,seeder 和 probeGenerator 是两个原子性变量,在初始化调用线程的种子 和 探针变量 时 会用到它们,每个线程只会使用一次
    变量 instance 是 ThreadLocalRandom 的一个实例,该变量是 static 的:

 /** The common ThreadLocalRandom */
    static final ThreadLocalRandom instance = new ThreadLocalRandom();

    当多线程通过 ThreadLocalRandom 的 current 方法获取 ThreadLocalRandom 实例时,其实 获取的是同一个实例 。但是由于具体的种子 是 存放在线程里面的,所以 ThreadLocalRandom 的实例里面只包含与线程无关的算法,所以它是线程安全的。
    

4、ThreadLocalRandom 主要代码逻辑

  • Unsafe 机制
  // 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 实例
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;

			// 获取 Thread 类中 threadLocalRandomSeed 变量在 Thread 实例里的偏移量 【种子】
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));

			// 获取 Thread 类中 threadLocalRandomProbe 变量在 Thread 实例里的偏移量 【探针】
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));

			// 获取 Thread 类中 threadLocalSecondarySeed 变量在 Thread 实例里的偏移量 【新种子】
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
  • current() 方法源码:
 public static ThreadLocalRandom current() {

		// 默认情况下,ThreadLocalRandomProbe 的值是 0
		// 如果 ThreadLocalRandomProbe 的值是 0,说明当前线程是第一次调用该方法
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
        
        	//(1) 需要初始化种子变量
            localInit();
        return instance;
    }

(1):

   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);
    }

    可以看出,该方法获取 ThreadLocalRandom 实例,并初始化调用线程中的 ThreadLocalRandomSeed 和 ThreadLocalRandomProbe 变量。
    求 probe 时,是根据 probeGenerator 计算 当前线程中 ThreadLocalRandomProbe 的初始化值。
     求 seed 时, 有个 seeder 变量,它的赋值是这样的:

 private static final AtomicLong seeder = new AtomicLong(initialSeed());

    进一步看 initialSeed() 方法,它是个静态方法:

 private static long initialSeed() {
        String pp = java.security.AccessController.doPrivileged(
                new sun.security.action.GetPropertyAction(
                        "java.util.secureRandomSeed"));
        if (pp != null && pp.equalsIgnoreCase("true")) {
            byte[] seedBytes = java.security.SecureRandom.getSeed(8);
            long s = (long)(seedBytes[0]) & 0xffL;
            for (int i = 1; i < 8; ++i)
                s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
            return s;
        }
        return (mix64(System.currentTimeMillis()) ^
                mix64(System.nanoTime()));
    }

    返回值是 System.currentTimeMillis() ,与当前时间相关,因此可以保证各线程生成的种子、新种子、随机数不同。
    而后,把 ThreadLocalRandomSeed 和 ThreadLocalRandomProbe 变量 设置到当前线程。
在这里插入图片描述

  • 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;

				//(1)
                 u = mix32(nextSeed()) >>> 1)
                ;
        }
        return r;
    }

(1):

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 变量中。
    
🎭 总结:
     ThreadLocalRandom 使用 ThreadLocal 的原理,让每个线程持有一个种子变量,该种子变量只有在使用随机数时 才会被初始化。在多线程下 计算新种子时 是根据自己线程内维护的种子变量进行更新,从而避免了竞争。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值