ThreadLocal线程局部变量

发布于 2022-02-11  1.99k 次阅读


 多线程的三大特征:

  1. 原子性:即一个操作或者多个操作要么全部执行并且执行的过程不会被其他因素打断
  2. 可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即得到修改的值
  3. 有序性:有序性即立即执行的顺序按照代码的先后顺序执行

注:多线程的特征是需要保证的,不是自带就有的,使用多线程时必须通过其他方式保证这几个特征

为了保证多线程的特征,java引入了多线程控制机制:

  • ThreadLocal:线程本地变量
  • 原子类:保证变量原子操作
  • lock:保证线程有序性
  • Volatile:保持线程变量可见性

一,threadLocal的概述

ThreadLocal提供线程局部变量,即为使用相同变量的每一个线程维护一个该变量的副本,当某些数据是以线程为作用域并且不同的线程拥有不同的数据副本时,就可以考虑采用ThreadLocal,比如数据库连接Connection,每一个请求处理线程都需要,但又不互相影响,就用ThreadLocal来实现

常用方法:

  • get():返回当前线程局部变量副本中的值
  • initialValue():返回此线程局部变量的当前线程的初始值
  • remove():删除此线程局部变量的当前线程的值
  • set():将当前线程的局部变量副本值设置为指定值
  • withInitial():创建线程局部变量

二,ThreadLocal的案例演示

案例:使用两个线程进行分别转账,每一个线程分别维护一个转账变量

public class bank {
       //内部类
       static class thread_local{
       
       ThreadLocal<Integer> local =  new ThreadLocal<Integer>() {
             //使用匿名内部类重写里面的方法
             //初始值为0
             protected Integer initialValue() {
               return 0;
           }};
             
           //取钱
             public Integer get() {
             
                    return local.get();
             }
                             
             //存钱
             public void set(Integer meney) {  
             
                    local.set(local.get()+meney);      
         }    
       
       }
       
       //内部类
       static class runnable_bank implements Runnable{
             thread_local local;
             
             public runnable_bank(thread_local local) {
                    this.local=local;
             }
             
             @Override
             public void run() {
                    for(int a=0;a<100;a++) {
                           local.set(10);
                           
                    }
                    System.out.println("账户"+Thread.currentThread().getName()+"余额:"+local.get());
             }
       }
       
       public static void main(String[] args) {
             
             thread_local local = new thread_local();
             
             //两个转账线程
             Thread thread1 = new Thread(new runnable_bank(local));
             Thread thread2 = new Thread(new runnable_bank(local));
             
             thread1.start();
             thread2.start();
             
             
       }
}
结果:

三,ThreadLocal的底层分析

ThreadLocal本质上只是一个操作的工具类,实际操作对象的ThreadLocalMap中的Entry数组,每一个线程维护一个ThreadLocalMap,更新的值保存到entry数组中

一,ThreadLocal的方法源码

1,set方法源码

public void set(T value) {
    //(1)获取当前线程(调用者线程)
    Thread t = Thread.currentThread();
    //(2)以当前线程作为key值,去查找对应的线程变量,找到对应的map
    ThreadLocalMap map = getMap(t);
    //(3)如果map不为null,就直接添加本地变量,key为当前定义的ThreadLocal变量的this引用,值为添加的本地变量值
    if (map != null)
        map.set(this, value);
    //(4)如果map为null,说明首次添加,需要首先创建出对应的map
    else
        createMap(t, value);
}

createMap(t,value)方法:创建一个ThreadLocalMap

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
}

2,get方法源码

   在get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null

public T get() {
    //(1)获取当前线程
    Thread t = Thread.currentThread();
    //(2)获取当前线程的threadLocals变量
    ThreadLocalMap map = getMap(t);
    //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量
    return setInitialValue();
}

3,remove()方法的源码

remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量

public void remove() {
    //获取当前线程绑定的threadLocals
     ThreadLocalMap m = getMap(Thread.currentThread());
     //如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
     if (m != null)
         m.remove(this);
}

二,ThreadLocalMap的源码

1,属性的设置

static class ThreadLocalMap {
    //Entry初始容量
    private static final int INITIAL_CAPACITY = 16;
    //Entry对象
    private ThreadLocal.ThreadLocalMap.Entry[] table;
    //Entry的大小
    private int size = 0;
    //阈值
    private int threshold;

    //阈值设置
    private void setThreshold(int len) {
        this.threshold = len * 2 / 3;
    }

    private static int nextIndex(int i, int len) {
        return i + 1 < len ? i + 1 : 0;
    }

    
    private static int prevIndex(int i, int len) {
        return i - 1 >= 0 ? i - 1 : len - 1;
    }

2,构造方法

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    this.table = new ThreadLocal.ThreadLocalMap.Entry[16];
    int i = firstKey.threadLocalHashCode & 15;
    this.table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);
    this.size = 1;
    this.setThreshold(16);
}


private ThreadLocalMap(ThreadLocal.ThreadLocalMap parentMap) {
    ThreadLocal.ThreadLocalMap.Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    this.setThreshold(len);
    this.table = new ThreadLocal.ThreadLocalMap.Entry[len];
    ThreadLocal.ThreadLocalMap.Entry[] var4 = parentTable;
    int var5 = parentTable.length;


    for(int var6 = 0; var6 < var5; ++var6) {
        ThreadLocal.ThreadLocalMap.Entry e = var4[var6];
        if (e != null) {
            ThreadLocal<Object> key = (ThreadLocal)e.get();
            if (key != null) {
                Object value = key.childValue(e.value);
                ThreadLocal.ThreadLocalMap.Entry c = new ThreadLocal.ThreadLocalMap.Entry(key, value);


                int h;
                for(h = key.threadLocalHashCode & len - 1; this.table[h] != null; h = nextIndex(h, len)) {
                }


                this.table[h] = c;
                ++this.size;
            }
        }
    }


}

三,Entry源码

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** value就是和ThreadLocal绑定的 */
    Object value;


    //k:ThreadLocal的引用,被传递给WeakReference的构造方法
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}
//WeakReference构造方法(public class WeakReference<T> extends Reference<T> )
public WeakReference(T referent) {
    super(referent); //referent:ThreadLocal的引用
}


//Reference构造方法
Reference(T referent) {
    this(referent, null);//referent:ThreadLocal的引用
}


Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}