1. 进程和线程
ThreadLocal是一个工具类,ThreadLocal是怎么那么产生的?
ThreadLoal是为了解决线程问题产生的,那么我们需要先了解下进程和线程。
所谓进程,是包括程序,资源的一次物理活动,进程需要内存中的程序,程序需要进行资源调度,包括IO,网络调用等。进程是早期计算机设计时候运行的一个基本单位。
对于线程来说,它比进程更加细粒化,线程是最小的运行单元我们可以把它当作一个迷你的进程,但是线程会更加灵活,线程涉及到资源共享。
当一套程序完成后,就可以进行处理,加载到内存中。相对于计算机,这套代码是“面向过程“的,因为它只是规范了一套流程。当计算机开始使用这套流程时,便会以最小的单位--线程来使用。正常情况下,每个线程都会运行同样的一段代码。这套流程会有自己的资源,资源对于这套流程是绝对安全的,因为这套流程是单线程。如果多个线程同时访问同一处资源,那么就会出现线程间的资源分配问题。资源访问何时给线程A,又何时给线程B,这些便是多线程环境下最棘手的点。多线程带来的挑战,无非就是正确处理资源的分配。
2.ThreadLocal是什么?
ThreadLocal可以用来解决线程中变量的保存和传递。由于Thread类维护了一个threadLocaMap的变量,所以在线程的生命周期开始之后,中间状态可以保存在线程的threadLocaMap中,由于每次获取threadLocaMap需要先获取线程实例,所以自然就做到了线程安全。
ThreadLocalmap维护了一个Entry数组,数据的定义如下
1 static class ThreadLocalMap { 2 static class Entry extends WeakReference<ThreadLocal<?>> { 3 /** The value associated with this ThreadLocal. */ 4 Object value; 5 6 Entry(ThreadLocal<?> k, Object v) { 7 super(k); 8 value = v; 9 } 10 } 11 12 private static final int INITIAL_CAPACITY = 16; 13 14 private Entry[] table; 15 16 private int size = 0; 17 18 private int threshold; // Default to 0 19 }
这里截取了部分的定义,ThreadLocaMap中维护了一个Entry数组,该Entry继承了WeakRefernce<?>,其中key值为当前threadLocal,value值为传入的threadLcoa.set传入的参数。可以看出,定义了几个threadLocal对象,entry数组中就会有几个实际使用的节点。ThreadLocal类的实现给我的感觉就是一个线程工具类,主要的工作都由ThreadLocalMap类来维护,ThreadLocal承担了获取线程,拿到ThreadLocalMap实例的职责。所以自己写了个简单的demo。
3. ThreadLocal仿写
1 /** 2 * @Author caizhenya 3 * @Date 2020/7/19 4 * @Descrition 5 **/ 6 public class MapUtil<T> { 7 8 private int gap = 1; 9 10 public void set(Object val) { 11 Thread thread = Thread.currentThread(); 12 MyThread myThread = null; 13 if (thread instanceof MyThread) { 14 myThread = (MyThread) thread; 15 } 16 17 assert myThread != null; 18 19 LocalVarMap localVarMap = myThread.getMap(); 20 if (localVarMap == null) { 21 create(myThread, val); 22 } else { 23 localVarMap.set(this, val); 24 } 25 } 26 27 public LocalVarMap getMap(MyThread myThread) { 28 return myThread.map; 29 } 30 31 public T get() { 32 Thread thread = Thread.currentThread(); 33 MyThread myThread = null; 34 if (thread instanceof MyThread) { 35 myThread = (MyThread) thread; 36 } 37 38 assert myThread != null; 39 LocalVarMap localVarMap = myThread.getMap(); 40 41 if (localVarMap == null) { 42 return null; 43 } 44 45 return (T) localVarMap.get(this); 46 } 47 48 public void create(MyThread myThread, Object val) { 49 ; 50 if (myThread.map == null) { 51 myThread.map = new LocalVarMap(); 52 myThread.map.create(); 53 myThread.map.entries[0] = new LocalVarMap.Entry(this, val); 54 } 55 } 56 57 static class LocalVarMap { 58 59 private int initalSize = 16; 60 61 private Entry[] entries; 62 63 public void create() { 64 entries = new Entry[initalSize]; 65 } 66 67 public void set(MapUtil<?> mapUtil, Object val) { 68 int size = initalSize; 69 for (int i = 0; i < size; i++) { 70 if (entries[i] == null) { 71 entries[i] = new Entry(mapUtil, val); 72 break; 73 } 74 75 } 76 } 77 78 public Object get(MapUtil<?> mapUtil) { 79 80 int size = initalSize; 81 for (int i = 0; i < size; i++) { 82 if (entries[i].get() != null && entries[i].get() == mapUtil) { 83 return entries[i].value; 84 } 85 } 86 return null; 87 } 88 89 static class Entry extends WeakReference<MapUtil<?>> { 90 private Object value; 91 92 public Entry(MapUtil<?> referent, Object value) { 93 super(referent); 94 this.value = value; 95 } 96 } 97 98 } 99 }
public class MyThread extends Thread { MapUtil.LocalVarMap map; public MyThread(Task task) { super(task); } public MapUtil.LocalVarMap getMap() { return this.map; } }
public class Task implements Runnable {
private MapUtil<String> mapUtil = new MapUtil<>();
private MapUtil<Object> objectMapUtil = new MapUtil<>();
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Override
public void run() {
mapUtil.set("mapUtil");
objectMapUtil.set(1L);
threadLocal.set("threadLocal");
System.out.println(Thread.currentThread().getName() + ",,," + mapUtil.get());
System.out.println(Thread.currentThread().getName() + ",,," + objectMapUtil.get());
System.out.println(Thread.currentThread().getName() + ",,," + threadLocal.get());
System.gc();
System.out.println(Thread.currentThread().getName() + ",,," + mapUtil.get());
System.out.println(Thread.currentThread().getName() + ",,," + objectMapUtil.get());
System.out.println(Thread.currentThread().getName() + ",,," + threadLocal.get());
}
}
public class Main { public static void main(String[] args) { MyThread myThread = new MyThread(new Task()); myThread.start(); } }
Entry继承了WeakReference,导致在每一次GC中,只要存在弱引用,都会回收弱引用的指向,这样会导致如果弱引用回收前维护的实例对象没有回收的情况下,发生内存泄漏,尤其是在线程复用的情况下发生频繁。所以在使用线程池等此类可以复用线程的场景下,需要手动调用threadLocal的remove方法,让Entry的value值可以被及时回收。
- 使用场景
ThreadLocal类的使用场景主要是线程变量维护以及传递的情况下用的多。比如生成,保存,获取用户的个人信息,传递对象状态等。