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 参考
0、JAVA多线程编程
Java多线程编程所涉及的知识点包含线程创建、线程同步、线程间通信、线程死锁、线程控制(挂起、停止和恢复)。之前
-
篇0
-
篇1
-
篇2
-
篇3
-
篇4
-
篇5
-
篇6
-
篇7
-
篇8