ThreadLocal会在每个线程内创建一个变量的副本对象,每个线程都是操作自己的那个变量
但是搜了一圈代码并没有发现好的代码示例,大部分都是讲原理和逻辑,有少部分写了代码的感觉自己都没搞懂,那个代码跑下来也不能证明ThreadLocal的功能
B站有up给出了工具类的ThreadLocal应用代码,感觉不错,但是比较复杂一些些。而且代码不完整
我找到了一个博主的代码,稍作修饰,感觉应该能够证明ThreadLocal的功能,并且我增加了一个对照组
原博如下:https://www.cnblogs.com/codechange/p/8652352.html
我的代码如下
package com.interview.bintest.ThreadLoca; import java.util.ArrayList; import java.util.List; /** * 测试ThreadLocal的功能 */ public class ThreadLocalTest { //首先创建一个全局静态变量ThreadLocal,这样无论创建多少个类对象,都只有一个ThreadLocal变量 private static ThreadLocal<List<String>> threadLocal = new ThreadLocal<>(); //设置一个存储ThreadLocal数据的方法 public void setThreadLocal(List<String> value) { threadLocal.set(value); } //设置一个获取ThreadLocal数据的方法,因为存储的是list集合,所以对集合也进行一下遍历 public void getThreadLocal() { threadLocal.get().forEach(name -> System.out.println(Thread.currentThread().getName()+"###" + name )); } //写一个main方法 public static void main(String[] args) throws InterruptedException { //创建一个包含ThreadLocal属性的类的实例 final ThreadLocalTest localTest = new ThreadLocalTest(); //创建第一个线程 new Thread(new Runnable() { @Override public void run() { //第一个线程创建一个list集合作为值存入threadlocal List<String> str1 = new ArrayList<String>(); //往第一个集合中存入字符串类型的数字 str1.add("1"); str1.add("2"); str1.add("3"); //将list集合存入threadlocal //因为只有一个localTest对象,也只有一个threadlocal,所以多个线程用的是一个类中相同的“threadlocal变量” localTest.setThreadLocal(str1); //提示已经存入成功 System.out.println("线程t1存入成功"); //存入之后线程睡3秒,保证多个线程都已经存入threadlocal再get()输出 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //遍历Threadlocal中的list localTest.getThreadLocal(); } },"t1").start(); //创建第二个线程 new Thread(new Runnable() { @Override public void run() { //和上面的线程相同,创建一个list集合存入 List<String> str2 = new ArrayList<String>(); //存入字符串类型的a和b str2.add("a"); str2.add("b"); //将list存入threadlocal //因为只有一个localTest对象,也只有一个threadlocal,所以多个线程用的是一个类中相同的“threadlocal变量” localTest.setThreadLocal(str2); //提示已经存入成功 System.out.println("t2已经存入成功"); //同样的线程睡3秒再输出 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } localTest.getThreadLocal(); } },"t2").start(); } }
最后的输出结果为
线程t1存入成功 t2已经存入成功 t1###1 t1###2 t1###3 t2###a t2###b
可以看到,用的同一个threadlocal的变量,但是里面的数据不共享
--------------------------------------------------------------对照组----------------------------------------------------------------------
我增加了一个对照组,使用的是HashMap,因为ThreadLocal底层用的是Entry数组,而HashMap底层用的是Node数组,是Entry的子类,两个是比较像的,而且ThreadLocal用于存储数据的那个内部类就叫做ThreadLocalMap
ThreadLocalMap的key值用的是当前线程的ThreadLocal对象,那我对照组就用当前线程名来作为key了
package com.interview.bintest.ThreadLoca; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Set; public class ThreadLocalControlTest { //首先创建一个全局静态变量HashMap,这样无论创建多少个类对象,都只有一个HashMap变量 private static HashMap<String,List<String>> hashMap = new HashMap<>(); //设置一个存储HashMap数据的方法 public void putHashMap(String name,List<String> value) { hashMap.put(name,value); } //设置一个获取HashMap数据的方法,因为存储的是list集合,所以对集合也进行一下遍历 public void getHashMap() { for(String name : hashMap.keySet()){ System.out.println(Thread.currentThread().getName()+"###"+hashMap.get(name)); } } //写一个main方法 public static void main(String[] args) throws InterruptedException { //创建一个包含HashMap属性的类的实例 final ThreadLocalControlTest localControlTest = new ThreadLocalControlTest(); //创建第一个线程 new Thread(new Runnable() { @Override public void run() { //第一个线程创建一个list集合作为值存入threadlocal List<String> str1 = new ArrayList<String>(); //往第一个集合中存入字符串类型的数字 str1.add("1"); str1.add("2"); str1.add("3"); //将list集合存入hashmap //因为只有一个localTest对象,也只有一个hashmap,所以多个线程用的是一个类中相同的“hashmap变量” localControlTest.putHashMap(Thread.currentThread().getName(),str1); //提示已经存入成功 System.out.println("线程t1存入成功"); //存入之后线程睡3秒,保证多个线程都已经存入threadlocal再get()输出 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //遍历Threadlocal中的list localControlTest.getHashMap(); } },"t1").start(); //创建第二个线程 new Thread(new Runnable() { @Override public void run() { //和上面的线程相同,创建一个list集合存入 List<String> str2 = new ArrayList<String>(); //存入字符串类型的a和b str2.add("a"); str2.add("b"); //将list存入hashmap //因为只有一个localTest对象,也只有一个hashmap,所以多个线程用的是一个类中相同的“hashmap变量” localControlTest.putHashMap(Thread.currentThread().getName(),str2); //提示已经存入成功 System.out.println("t2已经存入成功"); //同样的线程睡3秒再输出 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } localControlTest.getHashMap(); } },"t2").start(); } }
最终输出结果如下
线程t1存入成功 t2已经存入成功 t1###[1, 2, 3] t1###[a, b] t2###[1, 2, 3] t2###[a, b]
t1和t2输出的一模一样
tips:其实还可以在主线程中再输出一次存储数据的threadlocal和hashmap,这样也可以证实结论,因为一共有3个线程,主线程也可以把静态变量进行输出,对比效果也很明显,限于时间问题,我就不做测试了,有兴趣的可以自己测试
综上所述,ThreadLocal里的变量各个线程之间互不干扰的结论得到证实