ThreadLocal源代码1
1.类似于map,这个map存放的地方在线程里面,多个线程调用同一个threadLocal存储在不同的线程里面。
2.不是线程安全的。代码用=写的都是强引用,弱引用是虚拟机控制的,
public class ThreadLocalTrxt { static ThreadLocal<Object> x1 = new ThreadLocal<Object>(); static ThreadLocal<Object> x2 = new ThreadLocal<Object>(); static ThreadLocal<Object> x3 = new ThreadLocal<Object>(); static ThreadLocal<Object> x4 = new ThreadLocal<Object>(); static ThreadLocal<Object> x5 = new ThreadLocal<Object>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { for(int i = 0; i<5; i++) { x1.set(i); //把x1设置到这个线程的threadLocals里面的table里面的Entry里面的referent里面去,还有i=0 x2.set(i); x3.set(i); x4.set(i); x5.set(i); } Thread t = Thread.currentThread(); ThreadLocal<Object> x6 = new ThreadLocal<Object>(); x6.remove(); x1.remove(); x2.remove(); x3.remove(); x4.remove(); x5.remove(); } }).start(); } } }
public class Test { public static void main(String[] args) { AtomicInteger hashCode = new AtomicInteger(); int hash_increment = 0x61c88647; int size = 32; List <Integer> list = new ArrayList <> (); for (int i = 0; i < size; i++) { System.out.println(hashCode); hashCode.getAndAdd(hash_increment); System.out.println(hashCode); System.out.println(hashCode.intValue() & (size - 1)); list.add(hashCode.intValue() & (size - 1)); } System.out.println("original:" + list); Collections.sort(list); System.out.println("sort: " + list); System.out.println(hash_increment); } }
public class ThreadLocalTrxt { static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int s = new Random().nextInt(); x.set(s); // ThreadLocal 设置值的时候,自动跟线程关联 System.out.println(x.get()); // ThreadLocal 取值的时候,自动跟线程关联 } }).start(); } } }
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 返回null if (map != null) map.set(this, value); else createMap(t, value); } threadLocals是ThreadLocalMap在线程Thread里面 ThreadLocalMap getMap(Thread t) { return t.threadLocals; // 返回线程的threadLocals,是一个ThreadLocalMap类型,只不过这个类型在ThreadLocal里面定义 }
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); //this是ThreadLocal,调用x.set(s);的x } 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(); }
ThreadLocal设置值是设置到这个线程的threadLocals属性里面去了,并且ThreadLocal自己也在threadLocals里面。多个线程共用这个ThreadLocal。
比如用到了线程池,线程一直运行就不会终止,那么线程就会一直存在着,thereadLocalMap就会一直存在,map里面的key和value也会一直在,但是由于theadLocalMap里面的key=theadLocal是弱引用(没有强引用就会回收),key(theadLocal)一旦gc了,value因为是强引用就回收不了了,就发生了内存泄漏(value没有用了但是一直不能回收,就是内存泄漏)。解决办法:set,remove,rehash方法中,扫描到key=null的entry,并发对应的value设置为null。如果这个线程一直不终止,set,remove,rehash方法也一直不调用,那么还是会内存泄漏。解决办法:规定不再使用theadLocal的时候,一定要remove(),比如拦截器里面设置了user信息,那么线程出去的时候一定要把user给remove掉。
如果在每个线程中ThreadLocal.set()进去的东西本来就是多线程共享的同一个对象,比如static对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
例如在Spring中,如果可以使用RequestContextHolder,那么就不需要自己维护ThreadLocal,因为自己可能会忘记调用remove()方法等,造成内存泄漏。
DateTimeContextHolder类,RequestContextHolder类,看到里面用了ThreadLocal。
ThreadLocalMap的key是ThreadLocal,Value是set方法设置进来的值。
Entry是ThreadLocalMap的内部类,ThreadLocal和value2个属性组成。
线程初始化时候:
public class ThreadLocalTrxt { static ThreadLocal<Object> x1 = new ThreadLocal<Object>(); static ThreadLocal<Object> x2 = new ThreadLocal<Object>(); static ThreadLocal<Object> x3 = new ThreadLocal<Object>(); static ThreadLocal<Object> x4 = new ThreadLocal<Object>(); static ThreadLocal<Object> x5 = new ThreadLocal<Object>(); static ThreadLocal<Object> x6 = new ThreadLocal<Object>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() {//2个线程调用,则2个线程共享x1,x2,x3,x4,x5,x6。但是ThreadLocal里面没有属性(只是充当一个key),2个线程修改的时候, //修改的是线程内部的threadLocals。Thread和ThreadLocal是在一个包下的。不同线程相同的key(ThreadLocal)得到的可以是不同的value。 @Override public void run() { for(int i = 0; i<5; i++) { x1.set(i); //把x1设置到这个线程的threadLocals里面的table里面的Entry里面的referent里面去,还有i=0 x2.set(i); // 重复调用会覆盖 x3.set(i); x4.set(i); x5.set(i); x6.set(i); } Thread t = Thread.currentThread(); System.out.println(x1.get()); x1.remove(); } }).start(); } } }
ThreadLocal无法解决线程安全问题。也就是说ThreadLocal是被各个线程共享的。