• JAVA篇:Java 多线程 (五)ThreadLocal详解


    5 ThreadLocal详解

    关键字:ThreadLocal、InheritableThreadLocal、ThreadLocal和局部变量

    5.1 ThreadLocal

    ThreadLocal是一个泛型类,java.lang.ThreadLocal<T>

    这个类提供线程局部变量。可以将ThreadLocal定义为共享变量(全局变量或者static静态变量),每个线程在访问ThreadLocal变量(调用set/get)都会初始化属于线程的变量副本,可以用于存储一些与线程相关的状态、用户id、事务id等数据。

    可以通过子类重写initialValue()来设置初始值--默认初始值是null。

    5.1.1 ThreadLocal应用

    为线程设置递增的线程id,存在于ThreadLocal变量threadids中。

        /* 测试ThreadLocal */public void test1(){
            // 原子整数,线程安全
            AtomicInteger nextInt = new AtomicInteger();
            // 设定threadids的初始值递增
            ThreadLocal<Integer> threadids = new ThreadLocal<Integer>() {
                @Override
                protected Integer initialValue() {
                    return nextInt.incrementAndGet();//返回递增整数
                }
            };
    ​
            /* 创建5个子线程并输出threadid的值 */
    ​
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+": "+threadids.get());//在这个时候调用初始化并返回threadid
    ​
                }
            };
            int threadNum = 5;
            Thread[] threads = new Thread[threadNum];
    ​
            for(int i=0;i<threadNum;i++){
                threads[i] = new Thread(runnable);
    ​
            }
            /*倒序启动子线程*/
            for(int i=threadNum-1;i>=0;i--){
                threads[i].start();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    ​
            System.out.println(Thread.currentThread().getName()+": "+threadids.get());//在这个时候调用初始化并返回threadid
    ​
        }

    根据代码结果可以看出来threadids中对某个线程初始化其对应值的时机是该线程调用threadids.get或者threadids.set的时候,所以有如下结果:

    Thread-4: 1
    Thread-3: 2
    Thread-2: 3
    Thread-1: 4
    Thread-0: 5
    main: 6

    5.1.2 ThreadLocal.withInitial

    Java8中ThreadLocal对象提供了一个Lambda构造方式,实现了非常简洁的构造方法:withInitial

    这个方法采用Lambda方式传入实现了 Supplier 函数接口的参数。

            // 初始化1
            ThreadLocal<Integer> threadids1 = new ThreadLocal<>();
            System.out.println("使用new初始化的值:"+threadids1.get());
            //初始化2
            ThreadLocal<Integer> threadids2 = ThreadLocal.withInitial(()->100);
            System.out.println("使用withInitial初始化的值:"+threadids2.get());
            //初始化3
            ThreadLocal<HashMap<Integer,Integer>> threadids3 = ThreadLocal.withInitial(HashMap::new);
            System.out.println("使用withInitial初始化Map的值:"+threadids3.get());
    使用new初始化的值:null
    使用withInitial初始化的值:100
    使用withInitial初始化Map的值:{}

    5.2 InheritableThreadLocal

    ThreadLocal在父线程、子线程之间是完全独立不继承的。

    而InheritableThreadLocal给了子线程初始化继承父线程ThreadLocal变量值,以及父线程操作子线程初始化值的方法。但仅仅是初始化值的继承而已,InheritableThreadLocal对于线程依旧是线程独享的,子线程更改值并不影响父线程。

    子线程InheritableThreadLocal变量初始化的值默认继承子线程运行时父线程的值,但是也可以通过重写childValue方法来指定子线程初始值和父线程的值的关系。

        /* 测试InheritableThreadLocal */
        public void test3(){
            /* ThreadLocal */
            ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
            /* 默认继承父线程值的InheritableThreadLocal */
            InheritableThreadLocal<Integer> integerInheritableThreadLocal = new InheritableThreadLocal<>();
            /* 设置指定子线程值的InheritableThreadLocal */
            InheritableThreadLocal<Integer> integerInheritableThreadLocal2 = new InheritableThreadLocal<Integer>(){
                @Override
                protected Integer childValue(Integer value){
                    return value+10;
                }
            };
    ​
                    /* 主线程设置两个值都为1 */
            threadLocal.set(1);
            integerInheritableThreadLocal.set(1);
            integerInheritableThreadLocal2.set(1);
    ​
            /* 子线程中获取并更改值 */
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":threadLocal= "+threadLocal.get());
                    System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal="+integerInheritableThreadLocal.get());
                    System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal2="+integerInheritableThreadLocal2.get());
                    integerInheritableThreadLocal.set(2);
                    integerInheritableThreadLocal2.set(2);
                    System.out.println(Thread.currentThread().getName()+":设置 integerInheritableThreadLocal="+integerInheritableThreadLocal.get());
                    System.out.println(Thread.currentThread().getName()+":设置 integerInheritableThreadLocal2="+integerInheritableThreadLocal2.get());
    ​
                }
            };
    ​
            /* 创建子线程1 */
            Thread t1 = new Thread(runnable);
            t1.start();
            
    ​
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    ​
            /* 子线程的数据更改不会影响主线程 */
            System.out.println(Thread.currentThread().getName()+":threadLocal= "+threadLocal.get());
            System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal="+integerInheritableThreadLocal.get());
            System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal2="+integerInheritableThreadLocal.get());
        }
    ​
    
    Thread-0:threadLocal= null
    Thread-0:integerInheritableThreadLocal=1
    Thread-0:integerInheritableThreadLocal2=11
    Thread-0:设置 integerInheritableThreadLocal=2
    Thread-0:设置 integerInheritableThreadLocal2=2
    main:threadLocal= 1
    main:integerInheritableThreadLocal=1
    main:integerInheritableThreadLocal2=1

    5.3 ThreadLocal和局部变量

    变量线程独立,其实很容易想到局部变量。在一开始了解到ThreadLocal的定义时,我很难理解,因为我怎么想,这个定义都跟局部变量很像,我无法理解ThreadLocal设计出来的意义。

    按照某个说法,可以将局部变量看做是把钱放在自己家里,ThreadLocal变量则是把钱放在银行,虽然每个人各自的账号及钱也只能自己访问,但是钱放在一个同一的地方方便管理。这个区别不是共享变量和局部变量这样子访问权限上的区别,更大的区别在于一种设计上、代码上更加简单明了。

    5.X 参考

    Java中的ThreadLocal详解

    Java多线程9:ThreadLocal源码剖析

    Java多线程10:ThreadLocal的作用及使用

    Java ThreadLocal变量 - 什么时候用以及如何使用?

    ThreadLocal.withInitial

     

    0、JAVA多线程编程

    Java多线程编程所涉及的知识点包含线程创建、线程同步、线程间通信、线程死锁、线程控制(挂起、停止和恢复)。之前 JAVA篇:Java的线程仅仅了解了部分线程创建和同步相关的小部分知识点,但是其实在编程过程中遇到的事情并不仅仅限于此,所以进行整理,列表如下:

    当你深入了解,你就会发现世界如此广袤,而你对世界的了解则是如此浅薄,请永远保持谦卑的态度。
  • 相关阅读:
    20182311 2019-2020-1 《数据结构与面向对象程序设计》实验一报告
    20182311 2019-2020-1 《数据结构与面向对象程序设计》第1周学习总结
    20182320 2019-2020-1 《数据结构与面向对象程序设计》第2-3周学习总结
    20182320 2019-2020-1 《数据结构与面向对象程序设计》实验1报告
    预备作业
    mysql-sysbench编译安装过程问题
    mysql报错-ERROR 2002
    oracle中的root权限问题
    linux物理内存和虚拟内存
    shell-linux普通用户自动登录root脚本
  • 原文地址:https://www.cnblogs.com/liwxmyself/p/15420822.html
Copyright © 2020-2023  润新知