zoukankan      html  css  js  c++  java
  • 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并发编程之美》

  • 相关阅读:
    【Hadoop 分布式部署 五:分布式部署之分发、基本测试及监控】
    【Hadoop 分布式部署 四:配置Hadoop 2.x 中主节点(NN和RM)到从节点的SSH无密码登录】
    【Hadoop 分布式部署 三:基于Hadoop 2.x 伪分布式部署进行修改配置文件】
    【Hadoop 分布式部署 二:分布式环境预备工作(主机名 IP地址等设置)】
    【Hadoop 分布式部署 一 :分布式部署准备虚拟机 】
    Java基础 【类之间的关系】
    Java基础 【自动装箱和拆箱、面试题】
    java算法 第七届 蓝桥杯B组(题+答案) 3.凑算式
    java算法 第七届 蓝桥杯B组(题+答案) 2.生日蜡烛
    java算法 第七届 蓝桥杯B组(题+答案) 1.煤球数目
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12157842.html
Copyright © 2011-2022 走看看