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







  • 相关阅读:
    Silverlight 2 应用程序部署到任意HTML页面
    推荐一个工具包自定义HTTP 404错误
    WPF/Silverlight的UI和逻辑完全分离
    ObservableCollection 类
    Silverlight + ModelViewViewModel (MVVM)
    IIS7 request routing 和load balancing module发布
    DeepEarth:使用Silverlight的地图控件
    在Vista安装SQL 2008 Express遭遇属性不匹配错误解决办法
    RIA 应用程序模式
    WinForm界面开发之酒店管理系统报表篇
  • 原文地址:https://www.cnblogs.com/markytsai/p/13112746.html
Copyright © 2020-2023  润新知