threadlocal是什么?threadlocal 使得我们可以创建线程私有的变量, 这个变量相对于其他线程来说是不可见的,threadlocal为变量在每个线程中都创建了一个副本 , 每个线程可以访问自己私有的线程变量,代码示例如下 :
public class threadlocaldemo { //创建一个threadlocal对象,用来为每个线程会复制保存一份变量,实现线程封闭 private static threadlocal<integer> localnum = new threadlocal<integer>(){ @override protected integer initialvalue() { return 1; } }; public static void main(string[] args) { //线程0 new thread(){ @override public void run() { localnum.set(1); try { thread.sleep(2000); } catch (interruptedexception e) { e.printstacktrace(); } localnum.set(localnum.get()+10); system.out.println(thread.currentthread().getname()+":"+localnum.get());//11 } }.start(); //线程1 new thread(){ @override public void run() { localnum.set(3); try { thread.sleep(2000); } catch (interruptedexception e) { e.printstacktrace(); } localnum.set(localnum.get()+20); system.out.println(thread.currentthread().getname()+":"+localnum.get());//23 } }.start(); system.out.println(thread.currentthread().getname()+":"+localnum.get());//0 }}
如上所述, 算上main线程与新建的两个线程 ,总共三个线程 , 每个线程都包含自己的私有变量,此处我们设置值1 , set() 和 get() 方法用来设置值和获得值, 执行结果如下 :
threadlocal实现原理分析threadlocal是一个泛型类 , 可以接受任何类型的对象 , 其内部维护了一个threadlocalmap 的静态内部类, 我们使用的 get(), set()等其实都来自这个类, 每次都会为当前线程创建一个threadlocalmap对象, 用来记录私有的值
先看 set() 方法
public void set(t value) { //拿到当前线程 thread t = thread.currentthread(); //拿到当前线程map threadlocalmap map = getmap(t); if (map != null) //存在设置值 map.set(this, value); else //不存在则创建 createmap(t, value); }
void createmap(thread t, t firstvalue) { //threadlocals属性即为此map t.threadlocals = new threadlocalmap(this, firstvalue);}
接着是get() 方法
public t get() { //拿到当前线程 thread t = thread.currentthread(); //拿到当前线程对应的map threadlocalmap map = getmap(t); //如果已有map if (map != null) { //取值操作, 拿到对应的entry threadlocalmap.entry e = map.getentry(this); if (e != null) { @suppresswarnings("unchecked") t result = (t)e.value; return result; } } //没有map, 则去创建初始化一个map return setinitialvalue();}
private t setinitialvalue() { //initialvalue()方法返回的value为null t value = initialvalue(); //拿到当前线程去创建对应的map thread t = thread.currentthread(); threadlocalmap map = getmap(t); if (map != null) map.set(this, value); else createmap(t, value); return value;}
threadlocal可以理解为对threadlocalmap的封装
threadlocal内存泄漏问题在threadlocalmap中,threadlocal对象使用弱引用作为键
这样的话, 如果一个threadlocal不存在外部强引用时, 那么key注定要被gc回收 , 这样导致threadlocalmap 中key为null , 而value还存在着强引用链
一个线程可以同时拥有多个threadlocal, 如果作为弱引用的key被回收后, value还不能被回收,那么这就导致此threadlocal的生命周期和此线程是一样长的(因为线程执行完毕后此value的强引用链才会断), 如果线程一直不结束, 堆积的value也一直无法被回收, 那么就会产生内存泄漏问题
这里解决问题的方式是 : 每次使用完threadlocal后都调用它的remove()方法清除数据
public void remove() { threadlocalmap m = getmap(thread.currentthread()); if (m != null) m.remove(this);}
这里我们再来看一下key作为强弱引用的区别
如果key作为强引用, 那么它的生命周期和线程一样长,存在稳定的强引用链,无法被回收,产生内存泄漏问题, 而如果作为弱引用, gc则会自动的去回收它们, 在后续的remove()方法中也可以更好的去回收value , 所以我们一般将threadlocal设计成 private static 的, 在使用完后用remove()方法去手动删除它们
以上就是java中threadlocal线程变量的实现原理是什么的详细内容。
