java多线程---2.ThreadLocal类

一,概念

   它是一个线程的副本变量工具类,[工具类]java.lang.ThreadLocal.,当各个线程依赖同一个对象的时候,将这个对象复制隔离起来,互不影响的骚操作。也就是说,它应该是一个容器是吧,它不是一个线程!

    为什么要这样操作呢?如果说各个线程的操作互不影响,加锁又消耗效率,那么复制一份就是解决办法,由于java是引用类型,所以复制不简单,可能要序列化才能高度,所以这里推出一个工具类。

    ThreadLocal接口有四个方法:set get,remove,initialValue(下面有代码)。四个方法,在jdk1.5之后,该类还支持泛型。

二,实现原理

   它的类部有一个map,key对应每一个ThreadLocal实例(也就是this),value对应存放的值Object。

   参考链接: https://www.jianshu.com/p/98b68c97df9b

每个Thread 类中有一个成员名为:ThreadLocals ,它是ThreadLocal的内部类:ThreadLocalMap。

   /* ThreadLocal values pertaining to this thread. This map is  maintained
    * by the ThreadLocal class. */
   ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocal的内部类,

static class ThreadLocalMap {
       static class Entry extends WeakReference<ThreadLocal<?>> {
           Object value;
           Entry(ThreadLocal<?> k, Object v) {
               super(k);
               value = v;
           }
       }
       ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
           table = new Entry[INITIAL_CAPACITY];
           int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY -  1);
           table[i] = new Entry(firstKey, firstValue);
           size = 1;
           setThreshold(INITIAL_CAPACITY);
       }

....

    虽然map是Thread的成员但是它自己无法操作,而是由ThreadLocal进行管理,而且一个ThreadLocal里面一个线程只能放一个值,如果要放多个值,那么就需要多个ThreadLocal实例(因为Key就是线程标识,所以,第二次放的时候就会覆盖。)---》ThreadLocal提供的方法,get,set那么就是针对map(内部类)操作的,

   为什么要这样呢????

    因为这是内部类的特性,ThreadLocalMap所以属性都是私有的,外部的类只能引用,但是无法访问,访问权限只有本类才有,这里就有第三方的味道哦,参考链接: https://blog.csdn.net/zzg1229059735/article/details/82715741  说的很清楚。

threadlocal的操作方式:get()

public T get() {
   Thread t = Thread.currentThread();   //获得线程中的 map.
   ThreadLocalMap map = getMap(t);
   if (map != null) {
       ThreadLocalMap.Entry e = map.getEntry(this);
       if (e != null)
           return (T)e.value;
   }
   return setInitialValue();
}
//------------
ThreadLocalMap getMap(Thread t) {
   return t.threadLocals;
}
//------------
private T setInitialValue() {
   T value = initialValue();
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null)
       map.set(this, value);
   else
       createMap(t, value);
   return value;
}
//------------
void createMap(Thread t, T firstValue) {
   t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//------------
protected T initialValue() {
   return null;}

set()

public void set(T value) {
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);

   if (map != null)
       map.set(this, value);
   else
       createMap(t, value);
}

    remove()

public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
    m.remove(this);
}

  用例子来讲解三者之间的关系:用户就是Thread,而ThreadLocals(也就是map容器)  比如为银行卡,(银行卡里面存的钱,嘿嘿),而ThreadLocal比喻为银行,你拿着卡,什么都不能做(抛开网络支付什么的),你想要往卡里面存钱,只能去银行办理,你把卡给银行,钱给银行,银行帮你把钱存进去。那么这里就规定了,一个银行只能办理一个卡,如果你想要多个卡,那么就需要多个银行。一个银行卡签输了一个银行的Id.还有用户持有者的信息。

 银行就可以设置为单例的,或者静态的。不用重复生成。而卡是需要银行办理new出来的。

   所以这里把你的钱存进去,别人就看不到,拿不到, 就安全了,对于变量来说,也是一个道理,必须要要有线程的签名银行才进行操作,别的线程是无法访问的。

   ThrealLocalMap 中的map 是自己实现的,没有使用jdk的hashMap,它使用Entry类来存放一个节点,使用数组+哈希来形成检索方式(手动实现),从上面代码我们可以知道,entry也是一个内部类,它使用继承+泛型来实现键值对的操作。

  它是如何解决hash冲突的,由于它只是一个数组,而不是在但节点使用链表,所以它使用传统的解决hash方式,移位探索,而且还是一个一个的移动的。

       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);
       }

应用场景: 解决数据库连接、Session管理,每个线程访问数据库都应当是一个独立的Session会话,如果多个线程共享同一个Session会话,有可能其他线程关闭连接了,当前线程再执行提交时就会出现会话已关闭的异常,导致系统异常。此方式能避免线程争抢Session,提高并发下的安全性。

参考链接:https://zhuanlan.zhihu.com/p/82737256

存在的问题:内存泄漏。什么是内存遗漏,在gc算法里面提到,本来应该被回收的对象,没有回收,不应该回收的,被回收了,这就出现问题。

    ThreadLocal为什么会造成内存遗漏?:

ThreadLocalMap中的Entry的key是一个弱引用,所以,作为Key的ThreadLocal没有强引用,会被垃圾回收,下次使用的时候,就出现了Null异常,

如何解决:1.把ThreadLocal声明为Static,就不会被回收了。

            2.使用后,即使进行remove移除。

参考链接: https://www.jianshu.com/p/a1cd61fa22da

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值