ThreadLocal类和ThreadLocalRandom类

一、ThreadLocal类

ThreadLocal可以视作为一个工具类,它并不包含线程变量,线程变量都存储在各自Thread实例中ThreadLocals变量中,实现了线程变量内存隔离,保证线程安全(以空间换取的)。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类核心方法,set与get:实际控制Thread.currentThread().threadLocals对象来操作当前线程的变量

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    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 the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;//取当前线程Thread对象中的ThreadLocals变量
    }

实现:

/**
 * 优点:线程之间变量内存隔离,线程安全
 * 缺点:变量保存到当前线程中,线程不结束会一直占用内存,可能导致内存泄漏,需要调用remove清除;ThreadLocal保存到当前线程threadLocals中,不具有继承性,父线程的threadLocalMap取不到;
 */
public class ThreadLocalTest {

    static ThreadLocal<String> localVariable = new ThreadLocal<>();

    static void print(String str){
        System.out.println(str + ":" + localVariable.get());
        localVariable.remove();//内存泄漏    }

    public static void main(String[] args){
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("threadOne local variable");
                localVariable.set("threadOne local variable2");
                print("threadOne");
                System.out.println("threadOne remove after" + ":" + localVariable.get());
            }
        });
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                localVariable.set("threadTwo local variable");
                print("threadTwo");
                System.out.println("threadTwo remove after" + ":" + localVariable.get());
            }
        });
        threadOne.start();
        threadTwo.start();
    }
}

二、InheritableThreadLocal类

继承ThreadLocal类,主要解决第二个缺点ThreadLocal不具有继承性,父线程变量取不到

因此在Thread类中加入了inheritableThreadLocals用来存储父线程的变量的拷贝

注意:父线程变量拷贝是在Thread.init方法即创建线程时实现的,所以需要先赋值后创建子线程

        /*
         * Thread.init方法中
         */if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

实现:

public class InheritableThreadLocalTest {

    static ThreadLocal<String> inheritableLocal = new InheritableThreadLocal<>();

    static void print(String str){
        System.out.println(str + ": parents variable :" + inheritableLocal.get());
    }

    public static void main(String[] args) throws InterruptedException {
        inheritableLocal.set("mainThread variable");//父线程先赋值
        Thread threadOne = new Thread(new Runnable() {
            @Override
            public void run() {
                print("threadOne");
            }
        });
        inheritableLocal.remove();//变量拷贝所以threadOne中不会清除
        Thread threadTwo = new Thread(new Runnable() {
            @Override
            public void run() {
                print("threadTwo");
            }
        });

        threadOne.start();
        threadTwo.start();
    }
}

三、Random类

public class RandomTest {

    public static void main(String[] args){
        Random random = new Random();
        for (int i = 0; i < 10; i++){
            System.out.println(random.nextInt());
        }
    }
}
/*
 * nextInt() 源码
 */
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));//获取nextseed采用CAS保证原子性,多线程会使大量线程进行自旋重试,降低了并发性能。
return (int)(nextseed >>> (48 - bits)); }

四、ThreadLocalRandom类

继承了Random重写了nextInt,类似于ThreadLocal的实现,在各自Thread实例中保存自己的seed,具体声明源码如下。

    // The following three initially uninitialized fields are exclusively
    // managed by class java.util.concurrent.ThreadLocalRandom. These
    // fields are used to build the high-performance PRNGs in the
    // concurrent code, and we can not risk accidental false sharing.
    // Hence, the fields are isolated with @Contended.

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

    /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
    @sun.misc.Contended("tlr")
    int threadLocalRandomProbe;

    /** Secondary seed isolated from public ThreadLocalRandom sequence */
    @sun.misc.Contended("tlr")
    int threadLocalRandomSecondarySeed;

ThreadLocalRandom实现

public class ThreadLocalRandomTest {

    public static void main(String[] args){
        ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0;i < 10; i++){
            System.out.println(random.nextInt(5));
        }
    }
}

/**
 * nextseed获取源自各自线程中保存的seed,实现了线程隔离
 */
    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);//Thread.currentThread.threadLocalRandomSeed
        return r;
    }

ThreadLocalRandom主要源码模块

1.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 = 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);
        }
    }

2.构造方法私有化,提供静态方法current实例化,保证单例

    /** Constructor used only for static singleton */
    private ThreadLocalRandom() {
        initialized = true; // false during super() call
    }
    static final ThreadLocalRandom = new ThreadLocalRandom();
    /**
     * Returns the current thread's {@code ThreadLocalRandom}.*/
    public static ThreadLocalRandom current() {
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)//根据当前线程threadLocalRandomProbe标志,判断当前线程threadLocalRandomSeed是否已初始化
            localInit();
        return instance;
    }

    /**
     * Initialize Thread fields for the current thread.  Called only
     * when Thread.threadLocalRandomProbe is zero, indicating that a
     * thread local seed value needs to be generated. Note that even
     * though the initialization is purely thread-local, we need to
     * rely on (static) atomic generators to initialize the values.
     */
    static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; //初始化probe的值,p等于0时表示未初始化,所以skip掉
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));//初始化seed的值
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);//存到当前线程的threadLocalRandomSeed中
        UNSAFE.putInt(t, PROBE, probe);//
    }

3.随机数

    /**
     * Returns a pseudorandom {@code int} value between zero (inclusive)
     * and the specified bound (exclusive).
     *
     * @param bound the upper bound (exclusive).  Must be positive.
     * @return a pseudorandom {@code int} value between zero
     *         (inclusive) and the bound (exclusive)
     * @throws IllegalArgumentException if {@code bound} is not positive
     */
    public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        int r = mix32(nextSeed());//重点下一个seed
        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;
    }

    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);//取得是当前线程中的threadLocalRandomSeed变量
        return r;
    }
注意:初始化的seed值和probe值是写死的,nextSeed方法中累加的GAMMA也是写死的,所以多个线程第n次取随机数一样

参考《Java并发编程之美》

posted on 2020-01-06 18:35  FFStayF  阅读(258)  评论(0编辑  收藏  举报