性巴克一键去除衣物下的油渍:懒人福音,衣物焕新秘籍!

核心内容摘要

唐三与比比东的“不亦乐乎”MBA:一场关于权谋、成长与领导力的非凡对决
胸片检查,人人都能看得起的“健康之眼”——100%免费,只为守护您的健康!

www.91色萝网站樱花最新版-一起草_www.17.com网站

先看下ThreadLocal的基本用法创建5个线程给同一个ThreadLocal变量设置不同的值从打印结果看每个线程设置和获取的值都是不同的可见ThreadLocal为线程安全的每个线程保存的值相互独立。

public class ThreadLocalTest { public static ThreadLocal local new ThreadLocal(); public static void main(String[] args) { for (int i 0; i 5; i){ new Thread(new Runnable() { Override public void run() { local.set(hello Thread.currentThread()); System.out.println(local.get()); } }).start(); } } } //打印结果为: helloThread[Thread-4,5,main] helloThread[Thread-3,5,main] helloThread[Thread-2,5,main] helloThread[Thread-0,5,main] helloThread[Thread-1,5,main]现在开始ThreadLocal的原理过程比较复杂。

首先看下ThreadLocal的set()方法存数据的过程获取调用set方法的线程中持有的ThreadLocalMap(ThreadLocal.ThreadLocalMap threadLocals null;)每个线程的ThreadLocalMap都是独立的因此存储的值是不同的。

public void set(T value) { Thread t Thread.currentThread(); ThreadLocalMap map getMap(t); if (map ! null) { map.set(this, value); } else { createMap(t, value); } } // 获取线程内置的ThreadLocalMap ThreadLocalMap getMap(Thread t) { return t.threadLocals; }如果在一个线程中首次使用ThreadLocal保持数据则需要创建ThreadLocalMapThreadLocalMap中保存数据的实体是Entry保存数据的过程就是先计算这个ThreadLocal对象的hashcode根据hashcode计算在Entry数组中的位置然后将创建的Entry保存在这个位置。

void createMap(Thread t, T firstValue) { t.threadLocals new ThreadLocalMap(this, firstValue); } ThreadLocalMap(ThreadLocal? firstKey, Object firstValue) { table new Entry[INITIAL_CAPACITY]; int i firstKey.threadLocalHashCode (INITIAL_CAPACITY -

; table[i] new Entry(firstKey, firstValue); size 1; setThreshold(INITIAL_CAPACITY); } // 弱引用 static class Entry extends WeakReferenceThreadLocal? { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal? k, Object v) { super(k); value v; } }调用set()设置值的时候会根据ThreadLocal计算hashcode。

ThreadLocal中的属性threadLocalHashCodeprivate final int threadLocalHashCode nextHashCode();用来维护每个ThreadLocal的hash值。

再根据hashcode计算Entry数组的索引根据索引找到当前线程对应的Entry这里分三种情况第一次设值则直接添加如果是当前线程使用的ThreadLocalif (k key)则将对象设置进来即写到存储数据的Entry中ThreadLocalif (k key)已经有值了就直接更新ThreadLocal作为临时变量被gc了if (k null)例如在方法代码块中声明的ThreadLocal临时变量在方法执行完时不存在了Entry中的ThreadLocal作为key是弱引用就会被gc或者线程销毁了此时指向Entry的引用不存在了,Entry也会被gc此时如果不gc的话就会出现一块无法访问到的Entry造成内存泄漏。

set()时如果发现hash冲突ThreadLocal的做法是向后移动一位到数组的下个索引处保存Entry如果下个索引处有值了再继续向后找。

private void set(ThreadLocal? key, Object value) { Entry[] tab table; int len tab.length; int i key.threadLocalHashCode (len-

; for (Entry e tab[i]; e ! null; e tab[i nextIndex(i, len)]) { ThreadLocal? k e.get(); // 更新 if (k key) { e.value value; return; } // 临时ThreadLocal被回收的处理 if (k null) { replaceStaleEntry(key, value, i); return; } } // 第一次直接设置 tab[i] new Entry(key, value); int sz size; if (!cleanSomeSlots(i, sz) sz threshold) rehash(); } // 向后移动一位 private static int nextIndex(int i, int len) { return ((i 1 len) ? i 1 :

; }当通过get()方法获取数据时首先找到当前的线程对象获取线程对象内部的ThreadLocalMap然后根据ThreadLocal对象计算Entry的索引找到本线程存储数据的Entry获取Entry中的数据。

public T get() { Thread t Thread.currentThread(); ThreadLocalMap map getMap(t); if (map ! null) { ThreadLocalMap.Entry e map.getEntry(this); if (e ! null) { SuppressWarnings(unchecked) T result (T)e.value; return result; } } return setInitialValue(); } private Entry getEntry(ThreadLocal? key) { int i key.threadLocalHashCode (table.length -

; Entry e table[i]; if (e ! null e.get() key) return e; else return getEntryAfterMiss(key, i, e); }ThreadLocal内存泄漏的问题可以看到Entry是指向ThreadLocal的弱引用弱引用不会阻止gc的垃圾回收如果这个ThreadLocal对象置为null指向ThreadLocal对象的弱引用不会阻止gc的垃圾回收此时ThreadLocal对象会被gc回收通过get()方法获取value时需要计算ThreadLocal对象的hashcode在ThreadLocal对象被回收的情况就无法计算hashcode也就无法访问这个value引用的对象于是value就成了有引用链但是无法被访问的内存即造成内存泄漏了。

static class Entry extends WeakReferenceThreadLocal? { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal? k, Object v) { super(k); value v; } }解决方法将ThreadLocal变量定义成private static这样就一直存在ThreadLocal的强引用可以通过ThreadLocal对象访问到保存的数据不会造成内存泄漏调用remove()方法清除内存public void remove() { ThreadLocalMap m getMap(Thread.currentThread()); if (m ! null) { m.remove(this); } } private void remove(ThreadLocal? key) { Entry[] tab table; int len tab.length; int i key.threadLocalHashCode (len-

; for (Entry e tab[i]; e ! null; e tab[i nextIndex(i, len)]) { if (e.get() key) { e.clear(); expungeStaleEntry(i); return; } } }

总结一下每个ThreadLocal都有每个线程对应的ThreadLocalMap用于保存数据每个线程的ThreadLocalMap对象都不相同所以是线程安全的。

ThreadLocal存在内存泄漏问题需要持有ThreadLocal的强引用或remove清理。

有不对的地方请大神指出欢迎大家一起讨论交流共同进步更多请关注微信公众号 葡萄开源。

美国第1黄冈站-美国第1黄冈站应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123