ThreadLocal详解及仿写实例

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类的使用场景主要是线程变量维护以及传递的情况下用的多。比如生成,保存,获取用户的个人信息,传递对象状态等。







原文地址:https://www.cnblogs.com/markytsai/p/13112746.html