超简单的ThreadLocal
多线程访问同一个共享变量的时候容易出现并发问题,很多时候我们是通过加锁解决的。但是,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。那么这就要提到 ThreadLocal 了。比如很多公司做单点登录会用到 ThreadLocal ,通过拦截 token 去获取当前登录信息。
ThreadLocal<Integer> local = new ThreadLocal<>(); new Thread(()->{ local.set(7); System.out.println(local.get()); }).start(); new Thread(()->{ local.set(5); System.out.println(local.get()); }).start();
get/set
public void set(T value) { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的成员变量ThreadLocalMap对象 ThreadLocalMap map = getMap(t); //如果map不为空 if (map != null) //将值设置到map中,key是this,即threadLocal对象,value是传入的value值 map.set(this, value); else //如果map为空,则需要创建新的map对象 createMap(t, value); } public T get() { //获取当前线程 Thread t = Thread.currentThread(); //获取当前线程的成员变量ThreadLocalMap对象 ThreadLocalMap map = getMap(t); if (map != null) { //根据threadLocal对象从map中获取Entry对象 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") //获取保存的数据 T result = (T)e.value; return result; } } //初始化数据 return setInitialValue(); }
可以看到 ThreadLocal 的 get/set 就是操作的 ThreadLocalMap ,它里面有一个entrty[], entrty 用线程的引用做key,entrty 本身还继承了extends WeakReference 是一个弱引用,无论内存是否充足都会回收它,由此来解决内存溢出的问题。我们使用 ThreadLocal 的时候一般会 static,当弱引用被强引用使用的时候,这个弱引用则不会被回收,防止我们线程在使用时,threadlocal 被清空了。
resize
// 在 set 的时候会将 size+1,若 >= threshold 则执行 rehash(); // threshold = threshold * 2/3 private void set(ThreadLocal<?> key, Object value) { ..... int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } private void rehash() { // 扩容前先清理一波 key=null 的。 expungeStaleEntries(); // 如果回收之后的size大于等于threshold的3/4时,才需要真正的扩容 if (size >= threshold - threshold / 4) // resize中每次都是按2倍的大小扩容 resize(); }